本文的目的是在Bluekitch的btstack蓝牙host中增加苹果mfi iap2通道部分,也就是基于rfcomm增加MFI 的IAP2通道(不包含iap2 protocol/feature本身的部分)
一. 硬件环境
ubuntu 2004 + btstack + CSR8311

二. 软件环境
btstack编译
cd ~/project/btstack/iap2/add_iap2/port/posix-h4
make
btstack clean
cd ~/project/btstack/iap2/add_iap2/port/posix-h4
make clean
btstack运行
./iap2_demo -u /dev/ttyUSB1
三.代码验证
1. sdp注册

2. EIR注册

可以看到在iphone的carplay界面可以被搜索到,证明我们carplay uuid也是注册正确的

3. 被动连接




可以看到HCI连接被动连线成功,然后手机问询我们IAP2的rfcomm channel是1,然后rfcomm channel 1连线成功,也就是IAP2连线成功
4. 主动连接




可以看到HCI连接主动连线成功,然后我们问询手机IAP2的rfcomm channel是1,然后rfcomm channel 1连线成功,也就是IAP2连线成功
5. 断线功能



6. 发送/接收数据

发送数据部分


接收数据部分


可以看下IAP2 spec


四. 代码实现
1. API
uint8_t iap2_accessory_register_packet_handler(btstack_packet_handler_t callback);
uint8_t iap2_accessory_init(uint8_t rfcomm_channel_nr);
uint8_t iap2_accessory_deinit(void);
uint8_t iap2_accessory_create_sdp_record(uint8_t * service, uint32_t service_record_handle, int rfcomm_channel_nr,const char * name);
uint8_t iap2_accessory_create_eir_data(const char *name);
uint8_t iap2_accessory_establish_connection(bd_addr_t bd_addr);
uint8_t iap2_accessory_release_connection(hci_con_handle_t acl_handle);
uint8_t iap2_accessory_send_data(hci_con_handle_t acl_handle, uint8_t *data, uint16_t data_len);
2. callback
#define IAP2_SUBEVENT_CONNECTION_ESTABLISHED 0x01u
#define IAP2_SUBEVENT_CONNECTION_RELEASED 0x02u
#define IAP2_SUBEVENT_RECV_DATA 0x03u
3. 代码实现
我们先来看看增加了哪些差异点

a. example的修改
ⅰ. CMakeLists.txt中增加iap2_demo
# list of examples sorted by Bluetooth Mode configurations
# List of General Examples without Bluetooth
set(EXAMPLES_GENERAL
audio_duplex
led_counter
mod_player
sine_player
)
# List of Examples that only use Bluetooth BR/EDR = Classic
set(EXAMPLES_CLASSIC_ONLY
a2dp_sink_demo
a2dp_source_demo
avrcp_browsing_client
dut_mode_classic
gap_dedicated_bonding
gap_inquiry
gap_link_keys
hfp_ag_demo
hfp_hf_demo
hid_host_demo
hid_keyboard_demo
hid_mouse_demo
hsp_ag_demo
hsp_hs_demo
pbap_client_demo
sdp_bnep_query
sdp_general_query
sdp_rfcomm_query
spp_counter
spp_streamer
spp_streamer_client
ublox_spp_le_counter
iap2_demo
)
# List of Examples that only use Bluetooth LE
set(EXAMPLES_LE_ONLY
ancs_client_demo
att_delayed_response
gap_le_advertisements
gatt_battery_query
gatt_browser
gatt_counter
gatt_device_information_query
gatt_heart_rate_client
gatt_streamer_server
hog_boot_host_demo
hog_host_demo
hog_keyboard_demo
hog_mouse_demo
le_credit_based_flow_control_mode_client
le_credit_based_flow_control_mode_server
le_mitm
le_streamer_client
mesh_node_demo
nordic_spp_le_counter
nordic_spp_le_streamer
sm_pairing_central
sm_pairing_peripheral
ublox_spp_le_counter
)
# List of Examples that use Bluetooth BR/EDR/LE = Dual Mode
set(EXAMPLES_DUAL_MODE
gatt_counter
gatt_streamer_server
spp_and_gatt_counter
spp_and_gatt_streamer
)
# List of GATT files used by either LE_ONLY or DUAL_MODE examples
set(EXAMPLES_GATT_FILES
ancs_client_demo.gatt
att_delayed_response.gatt
gatt_battery_query.gatt
gatt_browser.gatt
gatt_counter.gatt
gatt_device_information_query.gatt
gatt_streamer_server.gatt
hog_host_demo.gatt
hog_keyboard_demo.gatt
hog_mouse_demo.gatt
le_credit_based_flow_control_mode_server.gatt
nordic_spp_le_counter.gatt
nordic_spp_le_streamer.gatt
sm_pairing_central.gatt
sm_pairing_peripheral.gatt
spp_and_gatt_counter.gatt
spp_and_gatt_streamer.gatt
ublox_spp_le_counter.gatt
)

ⅱ. Makefile.inc中增加iap2_demo的编译规则
VPATH += ${BTSTACK_ROOT}/src
VPATH += ${BTSTACK_ROOT}/src/ble
VPATH += ${BTSTACK_ROOT}/src/ble/gatt-service
VPATH += ${BTSTACK_ROOT}/src/classic
VPATH += ${BTSTACK_ROOT}/src/mesh
VPATH += ${BTSTACK_ROOT}/src/mesh/gatt-service
VPATH += ${BTSTACK_ROOT}/example
VPATH += ${BTSTACK_ROOT}/3rd-party/bluedroid/decoder/srce
VPATH += ${BTSTACK_ROOT}/3rd-party/bluedroid/encoder/srce
VPATH += ${BTSTACK_ROOT}/3rd-party/hxcmod-player
VPATH += ${BTSTACK_ROOT}/3rd-party/hxcmod-player
VPATH += ${BTSTACK_ROOT}/3rd-party/hxcmod-player/mods
VPATH += ${BTSTACK_ROOT}/3rd-party/lwip/core/src/core/
VPATH += ${BTSTACK_ROOT}/3rd-party/lwip/core/src/core/ipv4
VPATH += ${BTSTACK_ROOT}/3rd-party/lwip/core/src/core/ipv6
VPATH += ${BTSTACK_ROOT}/3rd-party/lwip/core/src/netif
VPATH += ${BTSTACK_ROOT}/3rd-party/lwip/core/src/apps/http
VPATH += ${BTSTACK_ROOT}/3rd-party/lwip/dhcp-server
VPATH += ${BTSTACK_ROOT}/3rd-party/md5
VPATH += ${BTSTACK_ROOT}/3rd-party/micro-ecc
VPATH += ${BTSTACK_ROOT}/3rd-party/yxml
VPATH += ${BTSTACK_ROOT}/platform/lwip
VPATH += ${BTSTACK_ROOT}/platform/lwip/port
CFLAGS += -I.
CFLAGS += -I${BTSTACK_ROOT}/src/ble
CFLAGS += -I${BTSTACK_ROOT}/src/classic
CFLAGS += -I${BTSTACK_ROOT}/src/mesh
CFLAGS += -I${BTSTACK_ROOT}/src
CFLAGS += -I${BTSTACK_ROOT}/3rd-party/bluedroid/decoder/include
CFLAGS += -I${BTSTACK_ROOT}/3rd-party/bluedroid/encoder/include
CFLAGS += -I${BTSTACK_ROOT}/3rd-party/hxcmod-player
CFLAGS += -I${BTSTACK_ROOT}/3rd-party/lwip/core/src/include/
CFLAGS += -I${BTSTACK_ROOT}/3rd-party/lwip/dhcp-server
CFLAGS += -I${BTSTACK_ROOT}/3rd-party/md5
CFLAGS += -I${BTSTACK_ROOT}/3rd-party/micro-ecc
CFLAGS += -I${BTSTACK_ROOT}/3rd-party/yxml
CFLAGS += -I${BTSTACK_ROOT}/platform/lwip
CFLAGS += -I${BTSTACK_ROOT}/platform/lwip/port
# for CVSD/SBC PLC
LDFLAGS += -lm
CORE += \
btstack_memory.c \
btstack_linked_list.c \
btstack_memory_pool.c \
btstack_run_loop.c \
btstack_util.c \
COMMON += \
ad_parser.c \
hci.c \
hci_cmd.c \
hci_dump.c \
hci_event.c \
l2cap.c \
l2cap_signaling.c \
btstack_audio.c \
btstack_tlv.c \
btstack_crypto.c \
uECC.c \
sm.c \
CLASSIC ?= \
sdp_util.c \
gatt_sdp.c \
spp_server.c \
rfcomm.c \
bnep.c \
sdp_server.c \
device_id_server.c \
SDP_CLIENT += \
sdp_client.o \
sdp_client_rfcomm.o \
ATT += \
att_dispatch.c \
GATT_SERVER += \
att_db.c \
att_server.c \
GATT_CLIENT += \
gatt_client.c \
battery_service_client.c \
device_information_service_client.c \
scan_parameters_service_client.c \
hids_client.c \
PAN += \
pan.c \
MBEDTLS = \
bignum.c \
ecp.c \
ecp_curves.c \
sm_mbedtls_allocator.c \
memory_buffer_alloc.c \
platform.c \
LWIP_CORE_SRC = init.c mem.c memp.c netif.c udp.c ip.c pbuf.c inet_chksum.c def.c tcp.c tcp_in.c tcp_out.c timeouts.c sys_arch.c
LWIP_IPV4_SRC = acd.c dhcp.c etharp.c icmp.c ip4.c ip4_frag.c ip4_addr.c
LWIP_NETIF_SRC = ethernet.c
LWIP_HTTPD = altcp_proxyconnect.c fs.c httpd.c
LWIP_SRC = ${LWIP_CORE_SRC} ${LWIP_IPV4_SRC} ${LWIP_NETIF_SRC} ${LWIP_HTTPD} dhserver.c
# List of files for Bluedroid SBC codec
include ${BTSTACK_ROOT}/3rd-party/bluedroid/decoder/Makefile.inc
include ${BTSTACK_ROOT}/3rd-party/bluedroid/encoder/Makefile.inc
SBC_CODEC = \
${SBC_DECODER} \
btstack_sbc_plc.c \
btstack_sbc_decoder_bluedroid.c \
${SBC_ENCODER} \
btstack_sbc_encoder_bluedroid.c \
hfp_msbc.c \
hfp_codec.c \
btstack_sbc_bluedroid.c
CVSD_PLC = \
btstack_cvsd_plc.c \
AVDTP += \
avdtp_util.c \
avdtp.c \
avdtp_initiator.c \
avdtp_acceptor.c \
avdtp_source.c \
avdtp_sink.c \
a2dp.c \
a2dp_source.c \
a2dp_sink.c \
btstack_ring_buffer.c \
AVRCP += \
avrcp.c \
avrcp_controller.c \
avrcp_target.c \
avrcp_browsing.c \
avrcp_browsing_controller.c \
avrcp_browsing_target.c \
avrcp_media_item_iterator.c \
HXCMOD_PLAYER = \
hxcmod.c \
nao-deceased_by_disease.c \
MESH = \
adv_bearer.c \
beacon.c \
gatt_bearer.c \
mesh.c \
mesh_access.c \
mesh_configuration_client.c \
mesh_configuration_server.c \
mesh_crypto.c \
mesh_foundation.c \
mesh_generic_default_transition_time_client.c \
mesh_generic_default_transition_time_server.c \
mesh_generic_level_client.c \
mesh_generic_level_server.c \
mesh_generic_on_off_client.c \
mesh_generic_on_off_server.c \
mesh_health_server.c \
mesh_iv_index_seq_number.c \
mesh_keys.c \
mesh_lower_transport.c \
mesh_network.c \
mesh_node.c \
mesh_peer.c \
mesh_provisioning_service_server.c \
mesh_proxy.c \
mesh_proxy_service_server.c \
mesh_upper_transport.c \
mesh_virtual_addresses.c \
pb_adv.c \
pb_gatt.c \
provisioning.c \
provisioning_device.c \
provisioning_provisioner.c \
# List of General Examples without Bluetooth
EXAMPLES_GENERAL = \
audio_duplex \
led_counter \
mod_player \
sine_player \
# List of Examples that only use Bluetooth BR/EDR = Classic
EXAMPLES_CLASSIC_ONLY = \
a2dp_sink_demo \
a2dp_source_demo \
avrcp_browsing_client \
dut_mode_classic \
gap_dedicated_bonding \
gap_inquiry \
gap_link_keys \
hfp_ag_demo \
hfp_hf_demo \
hid_host_demo \
hid_keyboard_demo \
hid_mouse_demo \
hsp_ag_demo \
hsp_hs_demo \
pbap_client_demo \
sdp_bnep_query \
sdp_general_query \
sdp_rfcomm_query \
spp_counter \
spp_streamer \
spp_streamer_client \
ublox_spp_le_counter \
iap2_demo \
# List of Examples that only use Bluetooth LE
EXAMPLES_LE_ONLY= \
ancs_client_demo \
att_delayed_response \
gap_le_advertisements \
gatt_battery_query \
gatt_browser \
gatt_counter \
gatt_device_information_query \
gatt_heart_rate_client \
gatt_streamer_server \
hog_boot_host_demo \
hog_host_demo \
hog_keyboard_demo \
hog_mouse_demo \
le_credit_based_flow_control_mode_client \
le_credit_based_flow_control_mode_server \
le_mitm \
le_streamer_client \
mesh_node_demo \
nordic_spp_le_counter \
nordic_spp_le_streamer \
sm_pairing_central \
sm_pairing_peripheral \
ublox_spp_le_counter \
# List of Examples that use Bluetooth BR/EDR/LE = Dual Mode
EXAMPLES_DUAL_MODE= \
gatt_counter \
gatt_streamer_server \
spp_and_gatt_counter \
spp_and_gatt_streamer \
# List of GATT files used by either LE_ONLY or DUAL_MODE examples
EXAMPLES_GATT_FILES = \
ancs_client_demo.gatt \
att_delayed_response.gatt \
gatt_battery_query.gatt \
gatt_browser.gatt \
gatt_counter.gatt \
gatt_device_information_query.gatt \
gatt_streamer_server.gatt \
hog_host_demo.gatt \
hog_keyboard_demo.gatt \
hog_mouse_demo.gatt \
le_credit_based_flow_control_mode_server.gatt \
nordic_spp_le_counter.gatt \
nordic_spp_le_streamer.gatt \
sm_pairing_central.gatt \
sm_pairing_peripheral.gatt \
spp_and_gatt_counter.gatt \
spp_and_gatt_streamer.gatt \
ublox_spp_le_counter.gatt \
# .h for .gatt
EXAMPLES_GATT_H_FILES = $(EXAMPLES_GATT_FILES:.gatt=.h)
# .o for .c
CORE_OBJ = $(CORE:.c=.o)
COMMON_OBJ = $(COMMON:.c=.o)
CLASSIC_OBJ = $(CLASSIC:.c=.o)
ATT_OBJ = $(ATT:.c=.o)
GATT_CLIENT_OBJ = $(GATT_CLIENT:.c=.o)
GATT_SERVER_OBJ = $(GATT_SERVER:.c=.o)
PAN_OBJ = $(PAN:.c=.o)
SBC_DECODER_OBJ = $(SBC_DECODER:.c=.o)
SBC_ENCODER_OBJ = $(SBC_ENCODER:.c=.o)
SBC_CODEC_OBJ = $(SBC_CODEC:.c=.o)
CVSD_PLC_OBJ = $(CVSD_PLC:.c=.o)
AVDTP_OBJ = $(AVDTP:.c=.o)
AVRCP_OBJ = $(AVRCP:.c=.o)
HXCMOD_PLAYER_OBJ = $(HXCMOD_PLAYER:.c=.o)
LWIP_OBJ = $(LWIP_SRC:.c=.o)
MESH_OBJ = $(MESH:.c=.o)
default_target: all
# compile .gatt descriptions
%.h: %.gatt
python3 ${BTSTACK_ROOT}/tool/compile_gatt.py $< $@
# examples
ant_test: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ant_test.c
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
sdp_rfcomm_query: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${PAN_OBJ} ${SDP_CLIENT} sdp_rfcomm_query.c
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
pbap_client_demo: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} md5.o obex_iterator.o obex_parser.o obex_message_builder.o goep_client.o yxml.o pbap_client.o pbap_client_demo.o
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
sdp_general_query: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} sdp_general_query.c
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
sdp_bnep_query: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} sdp_bnep_query.c
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
spp_counter: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} spp_counter.c
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
iap2_demo: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} iap2_demo.c iap2_accessory.o
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
att_delayed_response.o: att_delayed_response.h att_delayed_response.c
${CC} ${CFLAGS} -c $(filter-out att_delayed_response.h,$^) -o $@
att_delayed_response: ${CORE_OBJ} ${COMMON_OBJ} ${ATT_OBJ} ${GATT_SERVER_OBJ} att_delayed_response.o
${CC} $^ ${LDFLAGS} -o $@
hog_keyboard_demo.o: hog_keyboard_demo.h hog_keyboard_demo.c
${CC} ${CFLAGS} -c $(filter-out hog_keyboard_demo.h,$^) -o $@
hog_keyboard_demo: ${CORE_OBJ} ${COMMON_OBJ} ${ATT_OBJ} ${GATT_SERVER_OBJ} battery_service_server.o device_information_service_server.o btstack_hid_parser.o hids_device.o btstack_ring_buffer.o hog_keyboard_demo.o
${CC} $^ ${LDFLAGS} -o $@
hog_mouse_demo.o: hog_mouse_demo.h hog_mouse_demo.c
${CC} ${CFLAGS} -c $(filter-out hog_mouse_demo.h,$^) -o $@
hog_mouse_demo: ${CORE_OBJ} ${COMMON_OBJ} ${ATT_OBJ} ${GATT_SERVER_OBJ} battery_service_server.o device_information_service_server.o btstack_hid_parser.o hids_device.o hog_mouse_demo.o
${CC} $^ ${LDFLAGS} -o $@
hog_boot_host_demo: ${CORE_OBJ} ${COMMON_OBJ} ${ATT_OBJ} ${GATT_CLIENT_OBJ} hog_boot_host_demo.o
${CC} $^ ${LDFLAGS} -o $@
hog_host_demo.o: hog_host_demo.h hog_host_demo.c
${CC} ${CFLAGS} -c $(filter-out hog_host_demo.h,$^) -o $@
hog_host_demo: ${CORE_OBJ} ${COMMON_OBJ} ${ATT_OBJ} ${GATT_CLIENT_OBJ} ${ATT_OBJ} ${GATT_SERVER_OBJ} btstack_hid_parser.o btstack_hid.o hog_host_demo.o
${CC} $^ ${LDFLAGS} -o $@
sm_pairing_peripheral.o: sm_pairing_peripheral.h sm_pairing_peripheral.c
${CC} ${CFLAGS} -c $(filter-out sm_pairing_peripheral.h,$^) -o $@
sm_pairing_peripheral: ${CORE_OBJ} ${COMMON_OBJ} ${ATT_OBJ} ${GATT_SERVER_OBJ} ${GATT_CLIENT_OBJ} sm_pairing_peripheral.o
${CC} $^ ${LDFLAGS} -o $@
sm_pairing_central.o: sm_pairing_central.h sm_pairing_central.c
${CC} ${CFLAGS} -c $(filter-out sm_pairing_central.h,$^) -o $@
sm_pairing_central: ${CORE_OBJ} ${COMMON_OBJ} ${ATT_OBJ} ${GATT_SERVER_OBJ} ${GATT_CLIENT_OBJ} sm_pairing_central.o
${CC} $^ ${LDFLAGS} -o $@
gatt_counter.o: gatt_counter.h gatt_counter.c
${CC} ${CFLAGS} -c $(filter-out gatt_counter.h,$^) -o $@
gatt_counter: ${CORE_OBJ} ${COMMON_OBJ} ${ATT_OBJ} ${GATT_SERVER_OBJ} ${CLASSIC_OBJ} battery_service_server.o gatt_counter.o
${CC} $^ ${LDFLAGS} -o $@
gatt_streamer_server.o: gatt_streamer_server.h gatt_streamer_server.c
${CC} ${CFLAGS} -c $(filter-out gatt_streamer_server.h,$^) -o $@
gatt_streamer_server: ${CORE_OBJ} ${COMMON_OBJ} ${ATT_OBJ} ${GATT_SERVER_OBJ} ${CLASSIC_OBJ} gatt_streamer_server.o
${CC} $^ ${LDFLAGS} -o $@
le_streamer_client: ${CORE_OBJ} ${COMMON_OBJ} ${ATT_OBJ} ${GATT_CLIENT_OBJ} le_streamer_client.c
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
le_mitm: ${CORE_OBJ} ${COMMON_OBJ} le_mitm.c
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
gatt_heart_rate_client: ${CORE_OBJ} ${COMMON_OBJ} ${ATT_OBJ} ${GATT_CLIENT_OBJ} gatt_heart_rate_client.c
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
spp_and_gatt_counter.o: spp_and_gatt_counter.h spp_and_gatt_counter.c
${CC} ${CFLAGS} -c $(filter-out spp_and_gatt_counter.h,$^) -o $@
spp_and_gatt_counter: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${ATT_OBJ} ${GATT_SERVER_OBJ} spp_and_gatt_counter.o
${CC} $^ ${LDFLAGS} -o $@
spp_and_gatt_streamer.o: spp_and_gatt_streamer.h spp_and_gatt_streamer.c
${CC} ${CFLAGS} -c $(filter-out spp_and_gatt_streamer.h,$^) -o $@
spp_and_gatt_streamer: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${ATT_OBJ} ${GATT_SERVER_OBJ} spp_and_gatt_streamer.o
${CC} $^ ${LDFLAGS} -o $@
spp_streamer: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} spp_streamer.c
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
spp_flowcontrol: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} spp_flowcontrol.c
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
spp_streamer_client: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} spp_streamer_client.c
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
gap_dedicated_bonding: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} gap_dedicated_bonding.c
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
gap_inquiry: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} gap_inquiry.c
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
gap_link_keys: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} gap_link_keys.c
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
panu_demo: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} ${PAN_OBJ} panu_demo.c
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
pan_lwip_http_server: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} ${PAN_OBJ} ${LWIP_SRC} btstack_ring_buffer.o bnep_lwip.o pan_lwip_http_server.o
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
gatt_browser.o: gatt_browser.h gatt_browser.c
${CC} ${CFLAGS} -c $(filter-out gatt_browser.h,$^) -o $@
gatt_browser: ${CORE_OBJ} ${COMMON_OBJ} ${ATT_OBJ} ${GATT_CLIENT_OBJ} ${GATT_SERVER_OBJ} gatt_browser.o
${CC} $^ ${LDFLAGS} -o $@
gatt_battery_query.o: gatt_battery_query.h gatt_battery_query.c
${CC} ${CFLAGS} -c $(filter-out gatt_battery_query.h,$^) -o $@
gatt_battery_query: ${CORE_OBJ} ${COMMON_OBJ} ${ATT_OBJ} ${GATT_CLIENT_OBJ} ${GATT_SERVER_OBJ} gatt_battery_query.o
${CC} $^ ${LDFLAGS} -o $@
gatt_device_information_query.o: gatt_device_information_query.h gatt_device_information_query.c
${CC} ${CFLAGS} -c $(filter-out gatt_device_information_query.h,$^) -o $@
gatt_device_information_query: ${CORE_OBJ} ${COMMON_OBJ} ${ATT_OBJ} ${GATT_CLIENT_OBJ} ${GATT_SERVER_OBJ} gatt_device_information_query.o
${CC} $^ ${LDFLAGS} -o $@
ancs_client_demo.o: ancs_client_demo.h ancs_client_demo.c
${CC} ${CFLAGS} -c $(filter-out ancs_client_demo.h,$^) -o $@
ancs_client_demo: ${CORE_OBJ} ${COMMON_OBJ} ${ATT_OBJ} ${GATT_SERVER_OBJ} ${GATT_CLIENT_OBJ} ancs_client.o ancs_client_demo.o
${CC} $^ ${LDFLAGS} -o $@
led_counter: ${CORE_OBJ} ${COMMON_OBJ} led_counter.c
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
gap_le_advertisements: ${CORE_OBJ} ${COMMON_OBJ} gap_le_advertisements.c
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
hsp_hs_demo: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} ${SBC_CODEC_OBJ} ${CVSD_PLC_OBJ} wav_util.o sco_demo_util.o btstack_ring_buffer.o hsp_hs.o hsp_hs_demo.c
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
hsp_ag_demo: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} ${SBC_CODEC_OBJ} ${CVSD_PLC_OBJ} wav_util.o sco_demo_util.o btstack_ring_buffer.o hsp_ag.o hsp_ag_demo.c
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
hfp_ag_demo: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} ${SBC_CODEC_OBJ} ${CVSD_PLC_OBJ} wav_util.o sco_demo_util.o btstack_ring_buffer.o hfp.o hfp_gsm_model.o hfp_ag.o hfp_ag_demo.c
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
hfp_hf_demo: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} ${SBC_CODEC_OBJ} ${CVSD_PLC_OBJ} wav_util.o sco_demo_util.o btstack_ring_buffer.o hfp.o hfp_hf.o hfp_hf_demo.c
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
hid_host_demo: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} btstack_hid_parser.o hid_host.o hid_host_demo.o
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
hid_keyboard_demo: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} btstack_ring_buffer.o hid_device.o btstack_hid_parser.o hid_keyboard_demo.o
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
hid_mouse_demo: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} btstack_ring_buffer.o hid_device.o btstack_hid_parser.o hid_mouse_demo.o
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
a2dp_source_demo: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} ${SBC_CODEC_OBJ} ${AVDTP_OBJ} ${HXCMOD_PLAYER_OBJ} avrcp.o avrcp_controller.o avrcp_target.o a2dp_source_demo.c
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
a2dp_sink_demo: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} ${SBC_CODEC_OBJ} ${AVDTP_OBJ} avrcp.o avrcp_controller.o avrcp_target.o avrcp_cover_art_client.o goep_client.o obex_parser.o obex_message_builder.o btstack_resample.o btstack_sample_rate_compensation.o a2dp_sink_demo.c
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
avrcp_browsing_client: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} ${SDP_CLIENT} ${AVRCP_OBJ} ${AVDTP_OBJ} avrcp_browsing_client.c
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
dut_mode_classic: ${CORE_OBJ} ${COMMON_OBJ} ${CLASSIC_OBJ} dut_mode_classic.c
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
le_credit_based_flow_control_mode_client: ${CORE_OBJ} ${COMMON_OBJ} le_credit_based_flow_control_mode_client.c
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
le_credit_based_flow_control_mode_server.o: le_credit_based_flow_control_mode_server.h le_credit_based_flow_control_mode_server.c
${CC} ${CFLAGS} -c $(filter-out le_credit_based_flow_control_mode_server.h,$^) -o $@
le_credit_based_flow_control_mode_server: ${CORE_OBJ} ${COMMON_OBJ} ${ATT_OBJ} ${GATT_SERVER_OBJ} le_credit_based_flow_control_mode_server.o
${CC} $^ ${LDFLAGS} -o $@
mod_player: ${CORE_OBJ} ${COMMON_OBJ} ${HXCMOD_PLAYER_OBJ} btstack_audio.o mod_player.c
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
sine_player: ${CORE_OBJ} ${COMMON_OBJ} btstack_audio.o sine_player.c
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
audio_duplex: ${CORE_OBJ} ${COMMON_OBJ} btstack_audio.o btstack_ring_buffer.o audio_duplex.c
${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@
nordic_spp_le_counter.o: nordic_spp_le_counter.h nordic_spp_le_counter.c
${CC} ${CFLAGS} -c $(filter-out nordic_spp_le_counter.h,$^) -o $@
nordic_spp_le_counter: ${CORE_OBJ} ${COMMON_OBJ} ${ATT_OBJ} ${GATT_SERVER_OBJ} nordic_spp_service_server.o nordic_spp_le_counter.o
${CC} $^ ${LDFLAGS} -o $@
nordic_spp_le_streamer.o: nordic_spp_le_streamer.h nordic_spp_le_streamer.c
${CC} ${CFLAGS} -c $(filter-out nordic_spp_le_streamer.h,$^) -o $@
nordic_spp_le_streamer: ${CORE_OBJ} ${COMMON_OBJ} ${ATT_OBJ} ${GATT_SERVER_OBJ} nordic_spp_service_server.o nordic_spp_le_streamer.o
${CC} $^ ${LDFLAGS} -o $@
ublox_spp_le_counter.o: ublox_spp_le_counter.h ublox_spp_le_counter.c
${CC} ${CFLAGS} -c $(filter-out ublox_spp_le_counter.h,$^) -o $@
ublox_spp_le_counter: ${CORE_OBJ} ${COMMON_OBJ} ${ATT_OBJ} ${GATT_SERVER_OBJ} device_information_service_server.o ublox_spp_service_server.o ublox_spp_le_counter.o
${CC} $^ ${LDFLAGS} -o $@
mesh_node_demo.o: mesh_node_demo.h mesh_node_demo.c
${CC} ${CFLAGS} -c $(filter-out mesh_node_demo.h,$^) -o $@
mesh_node_demo:${CORE_OBJ} ${COMMON_OBJ} ${MESH_OBJ} ${ATT_OBJ} ${GATT_SERVER_OBJ} ${SM_OBJ} mesh_node_demo.o
${CC} $^ ${LDFLAGS} -o $@
clean:
rm -f ${EXAMPLES} ${EXAMPLES_GATT_H_FILES}
rm -f *.o *.out *.hex *.exe *.wav *.sbc
rm -rf *.dSYM
rm -rf ${BTSTACK_ROOT}/src/*.o
rm -rf ${BTSTACK_ROOT}/src/ble/*.o
rm -rf ${BTSTACK_ROOT}/src/ble/gatt-service/*.o
rm -rf ${BTSTACK_ROOT}/src/classic/*.o
rm -rf ${BTSTACK_ROOT}/example/*.o


ⅲ. iap2_demo.c的增加
#define BTSTACK_FILE__ "iap2_demo.c"
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "btstack.h"
#define IAP2_RFCOMM_SERVER_CHANNEL 1
#define IAP2_DEMO_DEVICE_NAME "IAP2_DEMO"
#define IAP2_SDP_NAME "IAP2_DEMO"
static uint16_t acl_handle = -1;
static bd_addr_t peer_addr = {0xB8,0x7B,0xC5,0xB5,0xA7,0xA7};
uint8_t iap2_detect_buf[] = {0xff,0x55,0x02,0x00,0xee,0x10};
static uint8_t iap2_service_buffer[150];
static btstack_packet_callback_registration_t hci_event_callback_registration;
#define MAX_COL 16U
#define SHOW_LINE_SIZE 16
static void bt_hex_dump(const uint8_t *data, uint32_t len)
{
uint32_t line;
uint32_t curline = 0U;
uint32_t curcol = 0U;
char showline[SHOW_LINE_SIZE];
uint32_t data_pos = 0U;
if ((len % MAX_COL) != 0U) {
line = (len / MAX_COL) + 1U;
} else {
line = len / MAX_COL;
}
for (curline = 0U; curline < line; curline++) {
(void) sprintf(showline, "%08xh:", curline * MAX_COL);
(void) printf("%s", showline);
for (curcol = 0; curcol < MAX_COL; curcol++) {
if (data_pos < len) {
(void) printf("%02x ", data[data_pos]);
data_pos++;
continue;
} else {
break;
}
}
(void) printf("\n");
}
}
#ifdef HAVE_BTSTACK_STDIN
// Testig User Interface
static void show_usage(void){
bd_addr_t iut_address;
gap_local_bd_addr(iut_address);
printf("\n--- Bluetooth IAP2 Accessory role unit Test Console %s ---\n", bd_addr_to_str(iut_address));
printf("\n");
printf("c - establish IAP2 channel to %s | ", bd_addr_to_str(peer_addr));
printf("d - release IAP2 connection to device\n");
printf("s - send detect data to iphone\n");
printf("\n");
}
static void stdin_process(char c){
char cmd = c; // used in packet handler
switch (cmd){
case 'c':
log_info("USER:\'%c\'", cmd);
printf("Establish iap2 connection to device with Bluetooth address %s...\n", bd_addr_to_str(peer_addr));
iap2_accessory_establish_connection(peer_addr);
break;
case 'd':
log_info("USER:\'%c\'", cmd);
printf("Release iap2 connection.\n");
iap2_accessory_release_connection(acl_handle);
break;
case 's':
log_info("USER:\'%c\'", cmd);
printf("send iap2 detect data.\n");
iap2_accessory_send_data(acl_handle, (uint8_t*) iap2_detect_buf, (uint16_t) sizeof(iap2_detect_buf));
break;
default:
show_usage();
break;
}
}
#endif
static void hci_packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
UNUSED(channel);
bd_addr_t event_addr;
switch (packet_type) {
case HCI_EVENT_PACKET:
switch (hci_event_packet_get_type(packet)) {
case HCI_EVENT_PIN_CODE_REQUEST:
// inform about pin code request
printf("Pin code request - using '0000'\n");
hci_event_pin_code_request_get_bd_addr(packet, event_addr);
gap_pin_code_response(event_addr, "0000");
break;
case HCI_EVENT_USER_CONFIRMATION_REQUEST:
// ssp: inform about user confirmation request
printf("SSP User Confirmation Request with numeric value '%06"PRIu32"'\n", little_endian_read_32(packet, 8));
printf("SSP User Confirmation Auto accept\n");
break;
default:
break;
}
break;
default:
break;
}
}
static void iap2_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t * event, uint16_t event_size){
UNUSED(channel);
uint8_t status;
bd_addr_t event_addr;
uint8_t *iap2_recv_data;
uint16_t data_len;
switch (packet_type){
case HCI_EVENT_PACKET:
switch (hci_event_packet_get_type(event)){
case HCI_EVENT_IAP2_META:
switch (hci_event_iap2_meta_get_subevent_code(event)) {
case IAP2_SUBEVENT_CONNECTION_ESTABLISHED:
status = iap2_subevent_connection_established_get_status(event);
if (status != ERROR_CODE_SUCCESS){
printf("Connection failed, status 0x%02x\n", status);
break;
}
acl_handle = iap2_subevent_connection_established_get_acl_handle(event);
iap2_subevent_connection_established_get_bd_addr(event, event_addr);
printf("IAP2 connection established %s.\n\n", bd_addr_to_str(event_addr));
break;
case IAP2_SUBEVENT_CONNECTION_RELEASED:
acl_handle = HCI_CON_HANDLE_INVALID;
printf("IAP2 connection released.\n\n");
break;
case IAP2_SUBEVENT_RECV_DATA:
iap2_recv_data = iap2_subevent_recv_data_get_data(event);
data_len = iap2_subevent_recv_data_get_data_len(event);
printf("IAP2 recv %d size data:\n",data_len);
bt_hex_dump(iap2_recv_data,data_len);
break;
default:
break;
}
break;
default:
break;
}
break;
default:
break;
}
}
int btstack_main(int argc, const char * argv[]);
int btstack_main(int argc, const char * argv[]){
(void)argc;
(void)argv;
l2cap_init();
rfcomm_init();
sdp_init();
iap2_accessory_init(IAP2_RFCOMM_SERVER_CHANNEL);
memset(iap2_service_buffer, 0, sizeof(iap2_service_buffer));
iap2_accessory_create_sdp_record(iap2_service_buffer, sdp_create_service_record_handle(), IAP2_RFCOMM_SERVER_CHANNEL, IAP2_SDP_NAME);
btstack_assert(de_get_len( iap2_service_buffer) <= sizeof(iap2_service_buffer));
sdp_register_service(iap2_service_buffer);
iap2_accessory_register_packet_handler(iap2_packet_handler);
gap_discoverable_control(1);
gap_ssp_set_io_capability(SSP_IO_CAPABILITY_DISPLAY_YES_NO);
gap_set_local_name(IAP2_DEMO_DEVICE_NAME);
/* must be called after gap_set_local_name,otherwise no name in EIR */
iap2_accessory_create_eir_data(IAP2_DEMO_DEVICE_NAME);
// - Set Class of Device - Service Class: Audio, Major Device Class: Audio, Minor: Hands-Free device
gap_set_class_of_device(0x200408);
// register for HCI events
hci_event_callback_registration.callback = &hci_packet_handler;
hci_add_event_handler(&hci_event_callback_registration);
#ifdef HAVE_BTSTACK_STDIN
btstack_stdin_setup(stdin_process);
#endif
// turn on!
hci_power_control(HCI_POWER_ON);
return 0;
}
/* EXAMPLE_END */
b. src的修改
ⅰ. btstack.h的修改

ⅱ. btstack_defines.h的修改


ⅲ. btstack_event.h的修改


/**
* @brief Get field acl_handle from event HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_ESTABLISHED
* @param event packet
* @return acl_handle
* @note: btstack_type H
*/
static inline hci_con_handle_t iap2_subevent_connection_established_get_acl_handle(const uint8_t * event){
return little_endian_read_16(event, 3);
}
/**
* @brief Get field status from event IAP2_SUBEVENT_CONNECTION_ESTABLISHED
* @param event packet
* @return status
* @note: btstack_type 1
*/
static inline uint8_t iap2_subevent_connection_established_get_status(const uint8_t * event){
return event[5];
}
/**
* @brief Get field bd_addr from event IAP2_SUBEVENT_CONNECTION_ESTABLISHED
* @param event packet
* @param Pointer to storage for bd_addr
* @note: btstack_type B
*/
static inline void iap2_subevent_connection_established_get_bd_addr(const uint8_t * event, bd_addr_t bd_addr){
reverse_bytes(&event[6], bd_addr, 6);
}
/**
* @brief Get field acl_handle from event IAP2_SUBEVENT_CONNECTION_RELEASED
* @param event packet
* @return acl_handle
* @note: btstack_type H
*/
static inline hci_con_handle_t iap2_subevent_connection_released_get_acl_handle(const uint8_t * event){
return little_endian_read_16(event, 3);
}
/**
* @brief Get field acl_handle from event IAP2_SUBEVENT_RECV_DATA
* @param event packet
* @return acl_handle
* @note: btstack_type H
*/
static inline uint16_t iap2_subevent_recv_data_get_data_len(const uint8_t * event){
return (event[1]-3);
}
/**
* @brief Get field acl_handle from event IAP2_SUBEVENT_RECV_DATA
* @param event packet
* @return acl_handle
* @note: btstack_type H
*/
static inline uint8_t* iap2_subevent_recv_data_get_data(const uint8_t * event){
return (uint8_t* )&event[5];
}
ⅳ. btstack_memory.h的修改


ⅴ. btstack_memory.c的修改

#ifdef MAX_NR_IAP2_CONNECTIONS
#if MAX_NR_IAP2_CONNECTIONS > 0
static iap2_connection_t iap2_connection_storage[MAX_NR_IAP2_CONNECTIONS];
static btstack_memory_pool_t iap2_connection_pool;
iap2_connection_t * btstack_memory_iap2_connection_get(void){
void * buffer = btstack_memory_pool_get(&iap2_connection_pool);
if (buffer){
memset(buffer, 0, sizeof(iap2_connection_t));
}
return (iap2_connection_t *) buffer;
}
void btstack_memory_iap2_connection_free(iap2_connection_t *iap2_connection){
btstack_memory_pool_free(&iap2_connection_pool, iap2_connection);
}
#else
iap2_connection_t * btstack_memory_iap2_connection_get(void){
return NULL;
}
void btstack_memory_iap2_connection_free(iap2_connection_t *iap2_connection){
UNUSED(iap2_connection);
};
#endif
#elif defined(HAVE_MALLOC)
typedef struct {
btstack_memory_buffer_t tracking;
iap2_connection_t data;
} btstack_memory_iap2_connection_t;
iap2_connection_t * btstack_memory_iap2_connection_get(void){
btstack_memory_iap2_connection_t * buffer = (btstack_memory_iap2_connection_t *) malloc(sizeof(btstack_memory_iap2_connection_t));
if (buffer){
memset(buffer, 0, sizeof(btstack_memory_iap2_connection_t));
btstack_memory_tracking_add(&buffer->tracking);
return &buffer->data;
} else {
return NULL;
}
}
void btstack_memory_iap2_connection_free(iap2_connection_t *iap2_connection){
// reconstruct buffer start
btstack_memory_buffer_t * buffer = &((btstack_memory_buffer_t *) iap2_connection)[-1];
btstack_memory_tracking_remove(buffer);
free(buffer);
}
#endif
ⅵ. classical下的Makefile.inc的修改

ⅶ. iap2_accessory.c .h的文件内容
/**
* @title IAP2 Accessory (IAP2)
*
*/
#ifndef BTSTACK_IAP2_ACCESSORY_H
#define BTSTACK_IAP2_ACCESSORY_H
#include "hci.h"
#if defined __cplusplus
extern "C" {
#endif
typedef enum {
IAP2_IDLE = 0, //0
IAP2_SDP_QUERY_RFCOMM_CHANNEL,
IAP2_W2_SEND_SDP_QUERY,
IAP2_W4_SDP_QUERY_COMPLETE,
IAP2_W4_RFCOMM_CONNECTED,
IAP2_W4_RFCOMM_DISCONNECTED,
} iap2_state_t;
typedef struct {
btstack_linked_item_t item;
bd_addr_t remote_addr;
hci_con_handle_t acl_handle;
uint8_t rfcomm_channel_nr;
uint16_t rfcomm_cid;
uint16_t rfcomm_mtu;
iap2_state_t state;
} iap2_connection_t;
uint8_t iap2_accessory_register_packet_handler(btstack_packet_handler_t callback);
uint8_t iap2_accessory_init(uint8_t rfcomm_channel_nr);
uint8_t iap2_accessory_deinit(void);
uint8_t iap2_accessory_create_sdp_record(uint8_t * service, uint32_t service_record_handle, int rfcomm_channel_nr,const char * name);
uint8_t iap2_accessory_create_eir_data(const char *name);
uint8_t iap2_accessory_establish_connection(bd_addr_t bd_addr);
uint8_t iap2_accessory_release_connection(hci_con_handle_t acl_handle);
uint8_t iap2_accessory_send_data(hci_con_handle_t acl_handle, uint8_t *data, uint16_t data_len);
/* API_END */
#if defined __cplusplus
}
#endif
#endif // BTSTACK_IAP2_ACCESSORY_H
#define BTSTACK_FILE__ "iap2_accessory.c"
// *****************************************************************************
//
// IAP2 Accessory (IAP2 ACC) unit
//
// *****************************************************************************
#include "btstack_config.h"
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include "bluetooth_sdp.h"
#include "btstack_debug.h"
#include "btstack_event.h"
#include "btstack_memory.h"
#include "btstack_run_loop.h"
#include "classic/core.h"
#include "classic/iap2_accessory.h"
#include "classic/sdp_client_rfcomm.h"
#include "classic/sdp_client.h"
#include "classic/sdp_server.h"
#include "classic/sdp_util.h"
#include "hci.h"
#include "hci_cmd.h"
#include "hci_dump.h"
#include "l2cap.h"
#include "bluetooth_data_types.h"
typedef struct {
bd_addr_t remote_address;
} iap2_sdp_query_context_t;
// const
static const char iap2_accessory_default_service_name[] = "Wireless IAP2";
static btstack_context_callback_registration_t iap2_sdp_query_request;
static iap2_sdp_query_context_t iap2_sdp_query_context;
static btstack_packet_handler_t iap2_accessory_callback;
static btstack_linked_list_t iap2_connections ;
const uint8_t iap2_accessory_uuid[16] = {
0x00, 0x00, 0x00, 0x00, 0xDE, 0xCA, 0xFA, 0xDE, 0xDE, 0xCA, 0xDE,0xAF, 0xDE, 0xCA, 0xCA, 0xFF
};
const uint8_t iap2_device_uuid[16] = {
0x00, 0x00, 0x00, 0x00, 0xDE, 0xCA, 0xFA, 0xDE, 0xDE, 0xCA, 0xDE, 0xAF, 0xDE, 0xCA, 0xCA, 0xFE
};
uint8_t iap2_eir_data_buf[EXTENDED_INQUIRY_RESPONSE_DATA_LEN] = {0};
uint8_t iap2_carplay_eir_uuid[] = {
/* UUID_SERVCLASS_IAP2_ACCESSORY UUID */
0xFF, 0xCA, 0xCA, 0xDE, 0xAF, 0xDE, 0xCA, 0xDE, 0xDE, 0xFA, 0xCA, 0xDE, 0x00, 0x00, 0x00, 0x00,
/* UUID_SERVCLASS_CARPLAY_ACCESSORY UUID */
0xEC, 0x88, 0x43, 0x48, 0xCD, 0x41, 0x40, 0xA2, 0x97, 0x27, 0x57, 0x5D, 0x50, 0xBF, 0x1F, 0xD3
};
btstack_linked_list_t * iap2_get_connections(void){
return (btstack_linked_list_t *) &iap2_connections;
}
iap2_connection_t * get_iap2_connection_context_for_rfcomm_cid(uint16_t cid){
btstack_linked_list_iterator_t it;
btstack_linked_list_iterator_init(&it, iap2_get_connections());
while (btstack_linked_list_iterator_has_next(&it)){
iap2_connection_t * iap2_connection = (iap2_connection_t *)btstack_linked_list_iterator_next(&it);
if (iap2_connection->rfcomm_cid == cid){
return iap2_connection;
}
}
return NULL;
}
iap2_connection_t * get_iap2_connection_context_for_bd_addr(bd_addr_t bd_addr){
btstack_linked_list_iterator_t it;
btstack_linked_list_iterator_init(&it, iap2_get_connections());
while (btstack_linked_list_iterator_has_next(&it)){
iap2_connection_t * iap2_connection = (iap2_connection_t *)btstack_linked_list_iterator_next(&it);
if (memcmp(iap2_connection->remote_addr, bd_addr, 6) == 0) {
return iap2_connection;
}
}
return NULL;
}
iap2_connection_t * get_iap2_connection_context_for_acl_handle(uint16_t handle){
btstack_linked_list_iterator_t it;
btstack_linked_list_iterator_init(&it, iap2_get_connections());
while (btstack_linked_list_iterator_has_next(&it)){
iap2_connection_t * iap2_connection = (iap2_connection_t *)btstack_linked_list_iterator_next(&it);
if (iap2_connection->acl_handle == handle){
return iap2_connection;
}
}
return NULL;
}
static iap2_connection_t * create_iap2_connection_context(void){
iap2_connection_t * iap2_connection = btstack_memory_iap2_connection_get();
if (!iap2_connection) return NULL;
iap2_connection->state = IAP2_IDLE;
iap2_connection->acl_handle = HCI_CON_HANDLE_INVALID;
btstack_linked_list_add(&iap2_connections, (btstack_linked_item_t*)iap2_connection);
return iap2_connection;
}
void iap2_finalize_connection_context(iap2_connection_t * iap2_connection){
btstack_linked_list_remove(&iap2_connections, (btstack_linked_item_t*) iap2_connection);
btstack_memory_iap2_connection_free(iap2_connection);
}
void iap2_emit_connection_event(iap2_connection_t * iap2_connection, uint8_t event_subtype, uint8_t value){
hci_con_handle_t acl_handle = (iap2_connection != NULL) ? iap2_connection->acl_handle : HCI_CON_HANDLE_INVALID;
uint8_t event[12];
event[0] = HCI_EVENT_IAP2_META;
event[1] = sizeof(event) - 2;
event[2] = event_subtype;
little_endian_store_16(event, 3, acl_handle);
event[5] = value; // status 0 == OK
reverse_bd_addr(iap2_connection->remote_addr,&event[6]);
(*iap2_accessory_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
}
void iap2_emit_recv_data_event(iap2_connection_t * iap2_connection, uint8_t *data, uint16_t data_len){
hci_con_handle_t acl_handle = (iap2_connection != NULL) ? iap2_connection->acl_handle : HCI_CON_HANDLE_INVALID;
uint8_t event[5+data_len];
event[0] = HCI_EVENT_IAP2_META;
event[1] = sizeof(event) - 2;
event[2] = IAP2_SUBEVENT_RECV_DATA;
little_endian_store_16(event, 3, acl_handle);
memcpy(&event[5], data, data_len);
(*iap2_accessory_callback)(HCI_EVENT_PACKET, 0, event, sizeof(event));
}
static iap2_connection_t * iap2_create_connection(bd_addr_t bd_addre){
iap2_connection_t * iap2_connection = get_iap2_connection_context_for_bd_addr(bd_addre);
if (iap2_connection) return iap2_connection;
iap2_connection = create_iap2_connection_context();
if (!iap2_connection) {
return NULL;
}
(void)memcpy(iap2_connection->remote_addr, bd_addre, 6);
log_info("Create IAP2 context %p: addr %s", iap2_connection, bd_addr_to_str(bd_addre));
return iap2_connection;
}
void iap2_handle_rfcomm_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
UNUSED(packet_type);
UNUSED(channel); // ok: no channel
UNUSED(size);
bd_addr_t event_addr;
uint16_t rfcomm_cid;
iap2_connection_t * iap2_connection = NULL;
log_debug("IAP2 packet_handler type %u, event type %x, size %u", packet_type, hci_event_packet_get_type(packet), size);
switch (hci_event_packet_get_type(packet)) {
case RFCOMM_EVENT_INCOMING_CONNECTION:
// data: event (8), len(8), address(48), channel (8), rfcomm_cid (16)
rfcomm_event_incoming_connection_get_bd_addr(packet, event_addr);
iap2_connection = iap2_create_connection(event_addr);
if (!iap2_connection){
log_info("iap2: no memory to accept incoming connection - decline");
rfcomm_decline_connection(rfcomm_event_incoming_connection_get_rfcomm_cid(packet));
return;
}
if (iap2_connection->state != IAP2_IDLE) {
log_error("iap2: incoming connection but not idle, reject");
rfcomm_decline_connection(rfcomm_event_incoming_connection_get_rfcomm_cid(packet));
return;
}
iap2_connection->rfcomm_cid = rfcomm_event_incoming_connection_get_rfcomm_cid(packet);
iap2_connection->state = IAP2_W4_RFCOMM_CONNECTED;
rfcomm_accept_connection(iap2_connection->rfcomm_cid);
break;
case RFCOMM_EVENT_CHANNEL_OPENED:
log_debug("IAP2 RFCOMM_EVENT_CHANNEL_OPENED");
rfcomm_event_channel_opened_get_bd_addr(packet, event_addr);
iap2_connection = get_iap2_connection_context_for_bd_addr(event_addr);
btstack_assert(iap2_connection != NULL);
if (iap2_connection->state != IAP2_W4_RFCOMM_CONNECTED){
break;
}
iap2_connection->acl_handle = rfcomm_event_channel_opened_get_con_handle(packet);
iap2_connection->rfcomm_cid = rfcomm_event_channel_opened_get_rfcomm_cid(packet);
iap2_connection->rfcomm_mtu = rfcomm_event_channel_opened_get_max_frame_size(packet);
bd_addr_copy(iap2_connection->remote_addr, event_addr);
rfcomm_request_can_send_now_event(iap2_connection->rfcomm_cid);
iap2_emit_connection_event(iap2_connection, IAP2_SUBEVENT_CONNECTION_ESTABLISHED, ERROR_CODE_SUCCESS);
break;
case RFCOMM_EVENT_CHANNEL_CLOSED:
log_debug("IAP2 RFCOMM_EVENT_CHANNEL_CLOSED");
rfcomm_cid = little_endian_read_16(packet,2);
iap2_connection = get_iap2_connection_context_for_rfcomm_cid(rfcomm_cid);
if (!iap2_connection) break;
iap2_emit_connection_event(iap2_connection, IAP2_SUBEVENT_CONNECTION_RELEASED, ERROR_CODE_SUCCESS);
iap2_finalize_connection_context(iap2_connection);
break;
default:
break;
}
}
static void iap2_accessory_rfcomm_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
iap2_connection_t * iap2_connection;
switch (packet_type){
case RFCOMM_DATA_PACKET:
iap2_connection = get_iap2_connection_context_for_rfcomm_cid(channel);
if (!iap2_connection) return;
/* Handle IAP2 RX data */
iap2_emit_recv_data_event(iap2_connection,packet,size);
return;
case HCI_EVENT_PACKET:
iap2_handle_rfcomm_event(packet_type, channel, packet, size);
break;
default:
break;
}
}
static void handle_query_rfcomm_event(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
UNUSED(packet_type);
UNUSED(channel);
UNUSED(size);
iap2_connection_t * iap2_connection = get_iap2_connection_context_for_bd_addr(iap2_sdp_query_context.remote_address);
if (iap2_connection == NULL) {
log_info("connection with %s not found", bd_addr_to_str(iap2_sdp_query_context.remote_address));
return;
}
switch (hci_event_packet_get_type(packet)){
case SDP_EVENT_QUERY_RFCOMM_SERVICE:
iap2_connection->rfcomm_channel_nr = sdp_event_query_rfcomm_service_get_rfcomm_channel(packet);
break;
case SDP_EVENT_QUERY_COMPLETE:
if(iap2_connection->rfcomm_channel_nr > 0)
{
iap2_connection->state = IAP2_W4_RFCOMM_CONNECTED;
rfcomm_create_channel(iap2_accessory_rfcomm_packet_handler, iap2_connection->remote_addr, iap2_connection->rfcomm_channel_nr, NULL);
}
else
{
uint8_t status = sdp_event_query_complete_get_status(packet);
if (status == ERROR_CODE_SUCCESS){
// report service not found
status = SDP_SERVICE_NOT_FOUND;
}
log_info("rfcomm service not found, status 0x%02x", status);
iap2_emit_connection_event(iap2_connection, IAP2_SUBEVENT_CONNECTION_RELEASED, status);
}
break;
default:
break;
}
}
static void iap2_handle_start_sdp_client_query(void * context){
UNUSED(context);
btstack_linked_list_iterator_t it;
btstack_linked_list_iterator_init(&it, &iap2_connections);
while (btstack_linked_list_iterator_has_next(&it)){
iap2_connection_t * connection = (iap2_connection_t *)btstack_linked_list_iterator_next(&it);
if (connection->state != IAP2_W2_SEND_SDP_QUERY) continue;
connection->state = IAP2_W4_SDP_QUERY_COMPLETE;
(void)memcpy(iap2_sdp_query_context.remote_address, connection->remote_addr, 6);
sdp_client_query_rfcomm_channel_and_name_for_uuid128(&handle_query_rfcomm_event, connection->remote_addr, iap2_device_uuid);
return;
}
}
uint8_t iap2_accessory_register_packet_handler(btstack_packet_handler_t callback)
{
btstack_assert(callback != NULL);
iap2_accessory_callback = callback;
return ERROR_CODE_SUCCESS;
}
uint8_t iap2_accessory_init(uint8_t rfcomm_channel_nr)
{
uint8_t status = rfcomm_register_service(iap2_accessory_rfcomm_packet_handler, rfcomm_channel_nr, 0xffff);
if (status != ERROR_CODE_SUCCESS){
return status;
}
return ERROR_CODE_SUCCESS;
}
uint8_t iap2_accessory_deinit(void)
{
iap2_accessory_callback = NULL;
return ERROR_CODE_SUCCESS;
}
uint8_t iap2_accessory_create_sdp_record(uint8_t * service, uint32_t service_record_handle, int rfcomm_channel_nr,const char * name)
{
uint8_t* attribute;
de_create_sequence(service);
// 0x0000 "Service Record Handle"
de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_SERVICE_RECORD_HANDLE);
de_add_number(service, DE_UINT, DE_SIZE_32, service_record_handle);
// 0x0001 "Service Class ID List"
de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_SERVICE_CLASS_ID_LIST);
attribute = de_push_sequence(service);
{
de_add_uuid128(attribute, (uint8_t *) iap2_accessory_uuid);
}
de_pop_sequence(service, attribute);
// 0x0004 "Protocol Descriptor List"
de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_PROTOCOL_DESCRIPTOR_LIST);
attribute = de_push_sequence(service);
{
uint8_t* l2cpProtocol = de_push_sequence(attribute);
{
de_add_number(l2cpProtocol, DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_L2CAP);
}
de_pop_sequence(attribute, l2cpProtocol);
uint8_t* rfcomm = de_push_sequence(attribute);
{
de_add_number(rfcomm, DE_UUID, DE_SIZE_16, BLUETOOTH_PROTOCOL_RFCOMM); // rfcomm_service
de_add_number(rfcomm, DE_UINT, DE_SIZE_8, rfcomm_channel_nr); // rfcomm channel
}
de_pop_sequence(attribute, rfcomm);
}
de_pop_sequence(service, attribute);
// 0x0005 "Public Browse Group"
de_add_number(service, DE_UINT, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_BROWSE_GROUP_LIST); // public browse group
attribute = de_push_sequence(service);
{
de_add_number(attribute, DE_UUID, DE_SIZE_16, BLUETOOTH_ATTRIBUTE_PUBLIC_BROWSE_ROOT );
}
de_pop_sequence(service, attribute);
// 0x0100 "ServiceName"
if (name == NULL){
name = iap2_accessory_default_service_name;
}
if (strlen(name) > 0){
de_add_number(service, DE_UINT, DE_SIZE_16, 0x0100);
de_add_data(service, DE_STRING, (uint16_t) strlen(name), (uint8_t *) name);
}
return ERROR_CODE_SUCCESS;
}
uint8_t iap2_accessory_create_eir_data(const char *name)
{
uint8_t index = 0;
memset(iap2_eir_data_buf,0,sizeof(iap2_eir_data_buf));
if(name != NULL)
{
iap2_eir_data_buf[index++] = strlen(name) + 1;
iap2_eir_data_buf[index++] = BLUETOOTH_DATA_TYPE_SHORTENED_LOCAL_NAME;
memcpy(iap2_eir_data_buf+index,name,strlen(name));
index += strlen(name);
}
iap2_eir_data_buf[index++] = sizeof(iap2_carplay_eir_uuid) + 1;
iap2_eir_data_buf[index++] = BLUETOOTH_DATA_TYPE_COMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS;
memcpy(iap2_eir_data_buf+index,iap2_carplay_eir_uuid,sizeof(iap2_carplay_eir_uuid));
gap_set_extended_inquiry_response(iap2_eir_data_buf);
return ERROR_CODE_SUCCESS;
}
uint8_t iap2_accessory_establish_connection(bd_addr_t bd_addr)
{
iap2_connection_t * connection = get_iap2_connection_context_for_bd_addr(bd_addr);
if (connection != NULL){
return ERROR_CODE_COMMAND_DISALLOWED;
}
connection = iap2_create_connection(bd_addr);
if (!connection){
return BTSTACK_MEMORY_ALLOC_FAILED;
}
connection->state = IAP2_W2_SEND_SDP_QUERY;
bd_addr_copy(connection->remote_addr, bd_addr);
iap2_sdp_query_request.callback = &iap2_handle_start_sdp_client_query;
// ignore ERROR_CODE_COMMAND_DISALLOWED because in that case, we already have requested an SDP callback
(void) sdp_client_register_query_callback(&iap2_sdp_query_request);
return ERROR_CODE_SUCCESS;
}
uint8_t iap2_accessory_release_connection(hci_con_handle_t acl_handle)
{
iap2_connection_t * connection = get_iap2_connection_context_for_acl_handle(acl_handle);
if (connection == NULL){
return ERROR_CODE_COMMAND_DISALLOWED;
}
connection->state = IAP2_W4_RFCOMM_DISCONNECTED;
rfcomm_disconnect(connection->rfcomm_cid);
return ERROR_CODE_SUCCESS;
}
uint8_t iap2_accessory_send_data(hci_con_handle_t acl_handle, uint8_t *data, uint16_t data_len)
{
iap2_connection_t * connection = get_iap2_connection_context_for_acl_handle(acl_handle);
if (connection == NULL){
return ERROR_CODE_COMMAND_DISALLOWED;
}
rfcomm_send(connection->rfcomm_cid, data, data_len);
return ERROR_CODE_SUCCESS;
}