bluekitchen btstack增加苹果MFI IAP2基于RFCOMM的通道协议部分

本文的目的是在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;
}