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/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 +++++ 152 files changed, 142368 insertions(+) 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 (limited to 'lib/bt/host/bluedroid/stack') 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 -- cgit v1.2.3