From 855bd1b03dd113344ce34fe3d5b0470acc5205c9 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Thu, 7 Jul 2016 15:24:59 +0200 Subject: [PATCH 1/5] l2cap: add LE Data Channel API --- src/l2cap.h | 95 ++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 87 insertions(+), 8 deletions(-) diff --git a/src/l2cap.h b/src/l2cap.h index 4010145c5..a79b10e85 100644 --- a/src/l2cap.h +++ b/src/l2cap.h @@ -279,19 +279,98 @@ int l2cap_send_prepared(uint16_t local_cid, uint16_t len); */ void l2cap_release_packet_buffer(void); -/* API_END */ -#if 0 +// +// LE Connection Oriented Channels feature with the LE Credit Based Flow Control Mode == LE Data Channel +// + -// to be implemented soon /** - * @brief Regster L2CAP LE Credit Based Flow Control Mode service - * @param + * @brief Register L2CAP LE Data Channel service + * @note MTU and initial credits are specified in l2cap_le_accept_connection(..) call + * @param packet_handler + * @param psm + * @param security_level */ -void l2cap_le_register_service(btstack_packet_handler_t packet_handler, uint16_t psm, - uint16_t mtu, uint16_t mps, uint16_t initial_credits, gap_security_level_t security_level); +void l2cap_le_register_service(btstack_packet_handler_t packet_handler, uint16_t psm, gap_security_level_t security_level); + +/** + * @brief Unregister L2CAP LE Data Channel service + * @param psm + */ + void l2cap_le_unregister_service(uint16_t psm); -#endif + +/* + * @brief Accept incoming LE Data Channel connection + * @param local_cid L2CAP LE Data Channel Identifier + * @param receive_buffer buffer used for reassembly of L2CAP LE Information Frames into service data unit (SDU) with given MTU + * @param receive_buffer_size buffer size equals MTU + * @param initial_credits Number of initial credits provided to peer + */ + +uint8_t l2cap_le_accept_connection(uint16_t local_cid, uint8_t * receive_sdu_buffer, uint16_t mtu, uint16_t initial_credits); + +/** + * @brief Deny incoming LE Data Channel connection due to resource constraints + * @param local_cid L2CAP LE Data Channel Identifier + */ + +uint8_t l2cap_le_decline_connection(uint16_t local_cid); + +/** + * @brief Create LE Data Channel connection + * @param packet_handler Packet handler for this connection + * @param address Peer address + * @param address_type Peer address type + * @param psm Service PSM to connect to + * @param receive_buffer buffer used for reassembly of L2CAP LE Information Frames into service data unit (SDU) with given MTU + * @param receive_buffer_size buffer size equals MTU + * @param initial_credits Number of initial credits provided to peer + * @param security_level Minimum required security level + * @param out_local_cid L2CAP LE Channel Identifier is stored here + */ +uint8_t l2cap_le_create_connection(btstack_packet_handler_t packet_handler, bd_addr_t address, bd_addr_type_t address_type, + uint16_t psm, uint8_t * receive_sdu_buffer, uint16_t mtu, uint16_t initial_credits, gap_security_level_t security_level, + uint16_t * out_local_cid); + +/** + * @brief Provide credtis for LE Data Channel + * @param local_cid L2CAP LE Data Channel Identifier + * @param credits Number additional credits for peer + */ +uint8_t l2cap_le_provide_credits(uint16_t cid, uint16_t credits); + +/** + * @brief Check if outgoing buffer is available and that there's space on the Bluetooth module + * @param local_cid L2CAP LE Data Channel Identifier + */ +int l2cap_le_can_send_now(uint16_t cid); + +/** + * @brief Request emission of L2CAP_EVENT_CAN_SEND_NOW as soon as possible + * @note L2CAP_EVENT_CAN_SEND_NOW might be emitted during call to this function + * so packet handler should be ready to handle it + * @param local_cid L2CAP LE Data Channel Identifier + */ +uint8_t l2cap_le_request_can_send_now_event(uint16_t cid); + +/** + * @brief Send data via LE Data Channel + * @note Since data larger then the maximum PDU needs to be segmented into multiple PDUs, data needs to stay valid until ... event + * @param local_cid L2CAP LE Data Channel Identifier + * @param data data to send + * @param size data size + */ +uint8_t l2cap_le_send_data(uint16_t cid, uint8_t * data, uint16_t size); + +/** + * @brief Disconnect from LE Data Channel + * @param local_cid L2CAP LE Data Channel Identifier + */ +uint8_t l2cap_le_disconnect(uint16_t cid); + +/* API_END */ #if defined __cplusplus } From ac7e9e28fd4ffbd7d233a9b307d29efcda4f4ac9 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Thu, 7 Jul 2016 17:01:49 +0200 Subject: [PATCH 2/5] l2cap: extract l2cap_create_channel_entry. implement register/unregister le service --- src/btstack_defines.h | 2 + src/l2cap.c | 229 +++++++++++++++++++++++++++++++----------- src/l2cap.h | 29 ++++-- 3 files changed, 191 insertions(+), 69 deletions(-) diff --git a/src/btstack_defines.h b/src/btstack_defines.h index 27d82699d..568183956 100644 --- a/src/btstack_defines.h +++ b/src/btstack_defines.h @@ -118,6 +118,8 @@ typedef uint8_t sm_key_t[16]; #define L2CAP_SERVICE_ALREADY_REGISTERED 0x69 #define L2CAP_DATA_LEN_EXCEEDS_REMOTE_MTU 0x6A +#define L2CAP_SERVICE_NOT_FOUND 0x6B +#define L2CAP_LOCAL_CID_INVALID 0x6C #define RFCOMM_MULTIPLEXER_STOPPED 0x70 #define RFCOMM_CHANNEL_ALREADY_REGISTERED 0x71 diff --git a/src/l2cap.c b/src/l2cap.c index 3d677c119..a2cc9bfca 100644 --- a/src/l2cap.c +++ b/src/l2cap.c @@ -772,6 +772,38 @@ static void l2cap_handle_remote_supported_features_received(l2cap_channel_t * ch channel->state = L2CAP_STATE_WILL_SEND_CONNECTION_REQUEST; } +static l2cap_channel_t * l2cap_create_channel_entry(btstack_packet_handler_t packet_handler, bd_addr_t address, bd_addr_type_t address_type, + uint16_t psm, uint16_t local_mtu, gap_security_level_t security_level){ + + l2cap_channel_t * channel = btstack_memory_l2cap_channel_get(); + if (!channel) { + return NULL; + } + + // Init memory (make valgrind happy) + memset(channel, 0, sizeof(l2cap_channel_t)); + + // fill in + channel->packet_handler = packet_handler; + bd_addr_copy(channel->address, address); + channel->address_type = address_type; + channel->psm = psm; + channel->local_mtu = local_mtu; + channel->remote_mtu = L2CAP_MINIMAL_MTU; + channel->required_security_level = security_level; + + // + channel->local_cid = l2cap_next_local_cid(); + channel->con_handle = 0; + + // set initial state + channel->state = L2CAP_STATE_WILL_SEND_CREATE_CONNECTION; + channel->state_var = L2CAP_CHANNEL_STATE_VAR_NONE; + channel->remote_sig_id = L2CAP_SIG_ID_INVALID; + channel->local_sig_id = L2CAP_SIG_ID_INVALID; + return channel; +} + /** * @brief Creates L2CAP channel to the PSM of a remote device with baseband address. A new baseband connection will be initiated if necessary. * @param packet_handler @@ -780,38 +812,19 @@ static void l2cap_handle_remote_supported_features_received(l2cap_channel_t * ch * @param mtu * @param local_cid */ -uint8_t l2cap_create_channel(btstack_packet_handler_t channel_packet_handler, bd_addr_t address, uint16_t psm, uint16_t mtu, uint16_t * out_local_cid){ - log_info("L2CAP_CREATE_CHANNEL addr %s psm 0x%x mtu %u", bd_addr_to_str(address), psm, mtu); + +uint8_t l2cap_create_channel(btstack_packet_handler_t channel_packet_handler, bd_addr_t address, uint16_t psm, uint16_t local_mtu, uint16_t * out_local_cid){ + log_info("L2CAP_CREATE_CHANNEL addr %s psm 0x%x mtu %u", bd_addr_to_str(address), psm, local_mtu); - // alloc structure - l2cap_channel_t * channel = btstack_memory_l2cap_channel_get(); + if (local_mtu > l2cap_max_mtu()) { + local_mtu = l2cap_max_mtu(); + } + + l2cap_channel_t * channel = l2cap_create_channel_entry(channel_packet_handler, address, BD_ADDR_TYPE_CLASSIC, psm, local_mtu, LEVEL_0); if (!channel) { return BTSTACK_MEMORY_ALLOC_FAILED; } - // Init memory (make valgrind happy) - memset(channel, 0, sizeof(l2cap_channel_t)); - // limit local mtu to max acl packet length - l2cap header - if (mtu > l2cap_max_mtu()) { - mtu = l2cap_max_mtu(); - } - - // fill in - bd_addr_copy(channel->address, address); - channel->psm = psm; - channel->con_handle = 0; - channel->packet_handler = channel_packet_handler; - channel->remote_mtu = L2CAP_MINIMAL_MTU; - channel->local_mtu = mtu; - channel->local_cid = l2cap_next_local_cid(); - - // set initial state - channel->state = L2CAP_STATE_WILL_SEND_CREATE_CONNECTION; - channel->state_var = L2CAP_CHANNEL_STATE_VAR_NONE; - channel->remote_sig_id = L2CAP_SIG_ID_INVALID; - channel->local_sig_id = L2CAP_SIG_ID_INVALID; - channel->required_security_level = LEVEL_0; - // add to connections list btstack_linked_list_add(&l2cap_channels, (btstack_linked_item_t *) channel); @@ -1081,25 +1094,17 @@ static void l2cap_handle_connection_request(hci_con_handle_t handle, uint8_t sig // alloc structure // log_info("l2cap_handle_connection_request register channel"); - l2cap_channel_t * channel = btstack_memory_l2cap_channel_get(); + l2cap_channel_t * channel = l2cap_create_channel_entry(service->packet_handler, hci_connection->address, BD_ADDR_TYPE_CLASSIC, + psm, service->mtu, service->required_security_level); if (!channel){ // 0x0004 No resources available l2cap_register_signaling_response(handle, CONNECTION_REQUEST, sig_id, 0x0004); return; } - // Init memory (make valgrind happy) - memset(channel, 0, sizeof(l2cap_channel_t)); - // fill in - bd_addr_copy(channel->address, hci_connection->address); - channel->psm = psm; + channel->con_handle = handle; - channel->packet_handler = service->packet_handler; - channel->local_cid = l2cap_next_local_cid(); channel->remote_cid = source_cid; - channel->local_mtu = service->mtu; - channel->remote_mtu = L2CAP_DEFAULT_MTU; channel->remote_sig_id = sig_id; - channel->required_security_level = service->required_security_level; // limit local mtu to max acl packet length - l2cap header if (channel->local_mtu > l2cap_max_mtu()) { @@ -1530,7 +1535,6 @@ uint8_t l2cap_register_service(btstack_packet_handler_t service_packet_handler, log_info("L2CAP_REGISTER_SERVICE psm 0x%x mtu %u", psm, mtu); // check for alread registered psm - // TODO: emit error event l2cap_service_t *service = l2cap_get_service(psm); if (service) { log_error("l2cap_register_service: PSM %u already registered", psm); @@ -1538,7 +1542,6 @@ uint8_t l2cap_register_service(btstack_packet_handler_t service_packet_handler, } // alloc structure - // TODO: emit error event service = btstack_memory_l2cap_service_get(); if (!service) { log_error("l2cap_register_service: no memory for l2cap_service_t"); @@ -1583,8 +1586,6 @@ void l2cap_register_fixed_channel(btstack_packet_handler_t the_packet_handler, u #ifdef ENABLE_BLE - -#if 0 static inline l2cap_service_t * l2cap_le_get_service(uint16_t psm){ return l2cap_get_service_internal(&l2cap_le_services, psm); } @@ -1592,33 +1593,26 @@ static inline l2cap_service_t * l2cap_le_get_service(uint16_t psm){ * @brief Regster L2CAP LE Credit Based Flow Control Mode service * @param */ -void l2cap_le_register_service(btstack_packet_handler_t packet_handler, uint16_t psm, - uint16_t mtu, uint16_t mps, uint16_t initial_credits, gap_security_level_t security_level){ +uint8_t l2cap_le_register_service(btstack_packet_handler_t packet_handler, uint16_t psm, gap_security_level_t security_level){ - log_info("L2CAP_LE_REGISTER_SERVICE psm 0x%x mtu %u connection %p", psm, mtu, connection); + log_info("L2CAP_LE_REGISTER_SERVICE psm 0x%x", psm); // check for alread registered psm - // TODO: emit error event l2cap_service_t *service = l2cap_le_get_service(psm); if (service) { - log_error("l2cap_le_register_service_internal: PSM %u already registered", psm); - l2cap_emit_service_registered(connection, L2CAP_SERVICE_ALREADY_REGISTERED, psm); - return; + return L2CAP_SERVICE_ALREADY_REGISTERED; } // alloc structure - // TODO: emit error event service = btstack_memory_l2cap_service_get(); if (!service) { log_error("l2cap_register_service_internal: no memory for l2cap_service_t"); - l2cap_emit_service_registered(connection, BTSTACK_MEMORY_ALLOC_FAILED, psm); - return; + return BTSTACK_MEMORY_ALLOC_FAILED; } // fill in service->psm = psm; - service->mtu = mtu; - service->mps = mps; + service->mtu = 0; service->packet_handler = packet_handler; service->required_security_level = security_level; @@ -1626,17 +1620,132 @@ void l2cap_le_register_service(btstack_packet_handler_t packet_handler, uint16_t btstack_linked_list_add(&l2cap_le_services, (btstack_linked_item_t *) service); // done - l2cap_emit_service_registered(connection, 0, psm); + return 0; } -void l2cap_le_unregister_service(uint16_t psm) { - +uint8_t l2cap_le_unregister_service(uint16_t psm) { log_info("L2CAP_LE_UNREGISTER_SERVICE psm 0x%x", psm); - l2cap_service_t *service = l2cap_le_get_service(psm); - if (!service) return; + if (!service) return L2CAP_SERVICE_NOT_FOUND; + btstack_linked_list_remove(&l2cap_le_services, (btstack_linked_item_t *) service); btstack_memory_l2cap_service_free(service); + return 0; } -#endif + +/* + * @brief Accept incoming LE Data Channel connection + * @param local_cid L2CAP LE Data Channel Identifier + * @param receive_buffer buffer used for reassembly of L2CAP LE Information Frames into service data unit (SDU) with given MTU + * @param receive_buffer_size buffer size equals MTU + * @param initial_credits Number of initial credits provided to peer + */ + +uint8_t l2cap_le_accept_connection(uint16_t local_cid, uint8_t * receive_sdu_buffer, uint16_t mtu, uint16_t initial_credits){ + return 0; +} + +/** + * @brief Deny incoming LE Data Channel connection due to resource constraints + * @param local_cid L2CAP LE Data Channel Identifier + */ + +uint8_t l2cap_le_decline_connection(uint16_t local_cid){ + return 0; +} + +/** + * @brief Create LE Data Channel + * @param packet_handler Packet handler for this connection + * @param address Peer address + * @param address_type Peer address type + * @param psm Service PSM to connect to + * @param receive_buffer buffer used for reassembly of L2CAP LE Information Frames into service data unit (SDU) with given MTU + * @param receive_buffer_size buffer size equals MTU + * @param initial_credits Number of initial credits provided to peer + * @param security_level Minimum required security level + * @param out_local_cid L2CAP LE Channel Identifier is stored here + */ +uint8_t l2cap_le_create_channel(btstack_packet_handler_t packet_handler, bd_addr_t address, bd_addr_type_t address_type, + uint16_t psm, uint8_t * receive_sdu_buffer, uint16_t mtu, uint16_t initial_credits, gap_security_level_t security_level, + uint16_t * out_local_cid) +{ + log_info("L2CAP_LE_CREATE_CHANNEL addr %s psm 0x%x mtu %u", bd_addr_to_str(address), psm, mtu); + + l2cap_channel_t * channel = l2cap_create_channel_entry(packet_handler, address, BD_ADDR_TYPE_CLASSIC, psm, mtu, LEVEL_0); + if (!channel) { + return BTSTACK_MEMORY_ALLOC_FAILED; + } + + // add to connections list + btstack_linked_list_add(&l2cap_le_channels, (btstack_linked_item_t *) channel); + + // store local_cid + if (out_local_cid){ + *out_local_cid = channel->local_cid; + } + + // check if hci connection is already usable + hci_connection_t * conn = hci_connection_for_bd_addr_and_type(address, address_type); + if (conn){ + log_info("l2cap_le_create_channel, hci connection already exists"); + // l2cap_handle_connection_complete(conn->con_handle, channel); + // check if remote supported fearures are already received + // if (conn->bonding_flags & BONDING_RECEIVED_REMOTE_FEATURES) { + // l2cap_handle_remote_supported_features_received(channel); + // } + } + + l2cap_run(); + + return 0; +} + +/** + * @brief Provide credtis for LE Data Channel + * @param local_cid L2CAP LE Data Channel Identifier + * @param credits Number additional credits for peer + */ +uint8_t l2cap_le_provide_credits(uint16_t cid, uint16_t credits){ + return 0; +} + +/** + * @brief Check if outgoing buffer is available and that there's space on the Bluetooth module + * @param local_cid L2CAP LE Data Channel Identifier + */ +int l2cap_le_can_send_now(uint16_t cid){ + return 0; +} + +/** + * @brief Request emission of L2CAP_EVENT_CAN_SEND_NOW as soon as possible + * @note L2CAP_EVENT_CAN_SEND_NOW might be emitted during call to this function + * so packet handler should be ready to handle it + * @param local_cid L2CAP LE Data Channel Identifier + */ +uint8_t l2cap_le_request_can_send_now_event(uint16_t cid){ + return 0; +} + +/** + * @brief Send data via LE Data Channel + * @note Since data larger then the maximum PDU needs to be segmented into multiple PDUs, data needs to stay valid until ... event + * @param local_cid L2CAP LE Data Channel Identifier + * @param data data to send + * @param size data size + */ +uint8_t l2cap_le_send_data(uint16_t cid, uint8_t * data, uint16_t size){ + return 0; +} + +/** + * @brief Disconnect from LE Data Channel + * @param local_cid L2CAP LE Data Channel Identifier + */ +uint8_t l2cap_le_disconnect(uint16_t cid) +{ + return 0; +} + #endif diff --git a/src/l2cap.h b/src/l2cap.h index a79b10e85..abf570699 100644 --- a/src/l2cap.h +++ b/src/l2cap.h @@ -112,8 +112,10 @@ typedef struct { L2CAP_CHANNEL_STATE_VAR state_var; // info - bd_addr_t address; hci_con_handle_t con_handle; + + bd_addr_t address; + bd_addr_type_t address_type; uint8_t remote_sig_id; // used by other side, needed for delayed response uint8_t local_sig_id; // own signaling identifier @@ -123,6 +125,8 @@ typedef struct { uint16_t local_mtu; uint16_t remote_mtu; + + uint16_t mps; // LE Data Channel uint16_t flush_timeout; // default 0xffff @@ -132,7 +136,16 @@ typedef struct { uint8_t reason; // used in decline internal uint8_t waiting_for_can_send_now; + + // credits for outgoing traffic + uint8_t credits_outgoing; + // number of packets remote will be granted + uint8_t new_credits_incoming; + + // credits for incoming traffic + uint8_t credits_incoming; + } l2cap_channel_t; // info regarding potential connections @@ -145,15 +158,13 @@ typedef struct { // incoming MTU uint16_t mtu; - - // incoming MPS - uint16_t mps; // internal connection btstack_packet_handler_t packet_handler; // required security level - gap_security_level_t required_security_level; + gap_security_level_t required_security_level; + } l2cap_service_t; @@ -292,14 +303,14 @@ void l2cap_release_packet_buffer(void); * @param psm * @param security_level */ -void l2cap_le_register_service(btstack_packet_handler_t packet_handler, uint16_t psm, gap_security_level_t security_level); +uint8_t l2cap_le_register_service(btstack_packet_handler_t packet_handler, uint16_t psm, gap_security_level_t security_level); /** * @brief Unregister L2CAP LE Data Channel service * @param psm */ -void l2cap_le_unregister_service(uint16_t psm); +uint8_t l2cap_le_unregister_service(uint16_t psm); /* * @brief Accept incoming LE Data Channel connection @@ -319,7 +330,7 @@ uint8_t l2cap_le_accept_connection(uint16_t local_cid, uint8_t * receive_sdu_buf uint8_t l2cap_le_decline_connection(uint16_t local_cid); /** - * @brief Create LE Data Channel connection + * @brief Create LE Data Channel * @param packet_handler Packet handler for this connection * @param address Peer address * @param address_type Peer address type @@ -330,7 +341,7 @@ uint8_t l2cap_le_decline_connection(uint16_t local_cid); * @param security_level Minimum required security level * @param out_local_cid L2CAP LE Channel Identifier is stored here */ -uint8_t l2cap_le_create_connection(btstack_packet_handler_t packet_handler, bd_addr_t address, bd_addr_type_t address_type, +uint8_t l2cap_le_create_channel(btstack_packet_handler_t packet_handler, bd_addr_t address, bd_addr_type_t address_type, uint16_t psm, uint8_t * receive_sdu_buffer, uint16_t mtu, uint16_t initial_credits, gap_security_level_t security_level, uint16_t * out_local_cid); From 9e6aabb55cefbebc59b42c59b0da39e3c9a5b14c Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 8 Jul 2016 16:48:35 +0200 Subject: [PATCH 3/5] pts: fix compile ble_central_test --- test/pts/ble_central_test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/pts/ble_central_test.c b/test/pts/ble_central_test.c index ee43168b3..d53654873 100644 --- a/test/pts/ble_central_test.c +++ b/test/pts/ble_central_test.c @@ -272,7 +272,7 @@ static void handle_advertising_event(uint8_t * packet, int size){ for (ad_iterator_init(&context, adv_size, (uint8_t *)adv_data) ; ad_iterator_has_more(&context) ; ad_iterator_next(&context)){ uint8_t data_type = ad_iterator_get_data_type(&context); // uint8_t size = ad_iterator_get_data_len(&context); - uint8_t * data = ad_iterator_get_data(&context); + const uint8_t * data = ad_iterator_get_data(&context); switch (data_type){ case 1: // AD_FLAGS if (*data & 1) printf("LE Limited Discoverable Mode, "); From 3f1d1cdaade66f848be40082735b81e8281880f8 Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 8 Jul 2016 16:48:52 +0200 Subject: [PATCH 4/5] pts: add le_data_channel test --- test/pts/Makefile | 5 +- test/pts/le_data_channel.c | 237 +++++++++++++++++++++++++++++++++++++ 2 files changed, 241 insertions(+), 1 deletion(-) create mode 100644 test/pts/le_data_channel.c diff --git a/test/pts/Makefile b/test/pts/Makefile index 2b34349cb..8bcf76beb 100644 --- a/test/pts/Makefile +++ b/test/pts/Makefile @@ -29,7 +29,7 @@ LDFLAGS += $(shell pkg-config libusb-1.0 --libs) # CFLAGS += -I/usr/local/include # LDFLAGS += -L/sw/lib -lportaudio -Wl,-framework,CoreAudio -Wl,-framework,AudioToolbox -Wl,-framework,AudioUnit -Wl,-framework,Carbon -EXAMPLES = iopt hfp_hf_test hfp_ag_test ble_peripheral_test ble_central_test l2cap_test classic_test bnep_test hsp_ag_test hsp_hs_test sco_loopback +EXAMPLES = iopt hfp_hf_test hfp_ag_test ble_peripheral_test ble_central_test l2cap_test classic_test bnep_test hsp_ag_test hsp_hs_test sco_loopback le_data_channel all: ${EXAMPLES} @@ -45,6 +45,9 @@ ble_peripheral_test: ble_peripheral_test.h ${CORE_OBJ} ${COMMON_OBJ} ${ATT_OBJ} ble_central_test: ble_central_test.h ${CORE_OBJ} ${COMMON_OBJ} ${SM_REAL_OBJ} ${ATT_OBJ} ${GATT_SERVER_OBJ} ${GATT_CLIENT_OBJ} ad_parser.o ble_central_test.c ${CC} $(filter-out ble_central_test.h,$^) ${CFLAGS} ${LDFLAGS} -o $@ +le_data_channel: ${CORE_OBJ} ${COMMON_OBJ} ${SM_REAL_OBJ} ${ATT_OBJ} ad_parser.o le_data_channel.c + ${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@ + hsp_ag_test: ${CORE_OBJ} ${COMMON_OBJ} ${SDP_CLIENT} hsp_ag.o hsp_ag_test.c ${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@ diff --git a/test/pts/le_data_channel.c b/test/pts/le_data_channel.c new file mode 100644 index 000000000..f05607216 --- /dev/null +++ b/test/pts/le_data_channel.c @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2014 BlueKitchen GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at + * contact@bluekitchen-gmbh.com + * + */ + +// ***************************************************************************** +/* EXAMPLE_START(ble_peripheral): LE Data Channel Test + * + */ +// ***************************************************************************** + +#include +#include +#include +#include +#include + +#include "btstack_config.h" + +#include "ble/le_device_db.h" +#include "ble/sm.h" +#include "btstack_debug.h" +#include "btstack_event.h" +#include "btstack_memory.h" +#include "btstack_run_loop.h" +#include "gap.h" +#include "hci.h" +#include "hci_dump.h" +#include "l2cap.h" +#include "stdin_support.h" + + static void show_usage(void); + +static btstack_packet_callback_registration_t hci_event_callback_registration; +static btstack_packet_callback_registration_t sm_event_callback_registration; + +static bd_addr_t pts_address = {0x00, 0x1B, 0xDC, 0x07, 0x32, 0xef}; +static int pts_address_type = 0; +static bd_addr_t master_address = {0x00, 0x1B, 0xDC, 0x07, 0x32, 0xef}; +static int master_addr_type = 0; +static hci_con_handle_t handle; +static uint32_t ui_passkey; +static int ui_digits_for_passkey; + +// general discoverable flags +static uint8_t adv_general_discoverable[] = { 2, 01, 02 }; + +const uint16_t psm_x = 0xf0; + +static void gap_run(void){ +} + +static void app_packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ + bd_addr_t event_address; + switch (packet_type) { + + case HCI_EVENT_PACKET: + switch (packet[0]) { + + case BTSTACK_EVENT_STATE: + // bt stack activated, get started + if (btstack_event_state_get_state(packet) == HCI_STATE_WORKING){ + show_usage(); + } + break; + + case HCI_EVENT_LE_META: + switch (hci_event_le_meta_get_subevent_code(packet)) { + case HCI_SUBEVENT_LE_CONNECTION_COMPLETE: + handle = little_endian_read_16(packet, 4); + printf("Connection handle 0x%04x\n", handle); + break; + + default: + break; + } + break; + + case HCI_EVENT_DISCONNECTION_COMPLETE: + break; + + case SM_EVENT_JUST_WORKS_REQUEST: + printf("SM_EVENT_JUST_WORKS_REQUEST\n"); + sm_just_works_confirm(little_endian_read_16(packet, 2)); + break; + + case SM_EVENT_PASSKEY_INPUT_NUMBER: + // display number + master_addr_type = packet[4]; + reverse_bd_addr(&packet[5], event_address); + printf("\nGAP Bonding %s (%u): Enter 6 digit passkey: '", bd_addr_to_str(master_address), master_addr_type); + fflush(stdout); + ui_passkey = 0; + ui_digits_for_passkey = 6; + break; + + case SM_EVENT_PASSKEY_DISPLAY_NUMBER: + // display number + printf("\nGAP Bonding %s (%u): Display Passkey '%06u\n", bd_addr_to_str(master_address), master_addr_type, little_endian_read_32(packet, 11)); + break; + + case SM_EVENT_PASSKEY_DISPLAY_CANCEL: + printf("\nGAP Bonding %s (%u): Display cancel\n", bd_addr_to_str(master_address), master_addr_type); + break; + + case SM_EVENT_AUTHORIZATION_REQUEST: + // auto-authorize connection if requested + sm_authorization_grant(little_endian_read_16(packet, 2)); + break; + + default: + break; + } + } + gap_run(); +} + +void show_usage(void){ + bd_addr_t iut_address; + uint8_t uit_addr_type; + + // gap_local_bd_addr(iut_address); + gap_advertisements_get_address(&uit_addr_type, iut_address); + + printf("\n--- CLI for LE Data Channel %s ---\n", bd_addr_to_str(iut_address)); + printf("a - connect to type %u address %s PSM 0x%02x\n", pts_address_type, bd_addr_to_str(pts_address), psm_x); + printf("---\n"); + printf("Ctrl-c - exit\n"); + printf("---\n"); +} +static uint8_t buffer_x[1000]; +static uint16_t cid_x; + +static void stdin_process(btstack_data_source_t *ds, btstack_data_source_callback_type_t callback_type){ + char buffer; + read(ds->fd, &buffer, 1); + + // passkey input + if (ui_digits_for_passkey){ + if (buffer < '0' || buffer > '9') return; + printf("%c", buffer); + fflush(stdout); + ui_passkey = ui_passkey * 10 + buffer - '0'; + ui_digits_for_passkey--; + if (ui_digits_for_passkey == 0){ + printf("\nSending Passkey '%06x'\n", ui_passkey); + sm_passkey_input(handle, ui_passkey); + } + return; + } + + switch (buffer){ + case 'a': + printf("Creating connection to %s\n", bd_addr_to_str(pts_address)); + l2cap_le_create_channel(&app_packet_handler,pts_address, pts_address_type, psm_x, buffer_x, sizeof(buffer_x), 1, LEVEL_0, &cid_x); + break; + + default: + show_usage(); + break; + + } + return; +} + +int btstack_main(int argc, const char * argv[]); +int btstack_main(int argc, const char * argv[]){ + + printf("BTstack LE Data Channel test starting up...\n"); + + // register for HCI events + hci_event_callback_registration.callback = &app_packet_handler; + hci_add_event_handler(&hci_event_callback_registration); + + // set up l2cap_le + l2cap_init(); + + // setup le device db + le_device_db_init(); + + // setup SM: Display only + sm_init(); + sm_set_io_capabilities(IO_CAPABILITY_DISPLAY_ONLY); + sm_set_authentication_requirements( SM_AUTHREQ_BONDING | SM_AUTHREQ_MITM_PROTECTION); + sm_event_callback_registration.callback = &app_packet_handler; + sm_add_event_handler(&sm_event_callback_registration); + + btstack_stdin_setup(stdin_process); + + gap_random_address_set_update_period(5000); + gap_random_address_set_mode(GAP_RANDOM_ADDRESS_RESOLVABLE); + gap_advertisements_set_data(sizeof(adv_general_discoverable), adv_general_discoverable); + sm_set_io_capabilities(IO_CAPABILITY_NO_INPUT_NO_OUTPUT); + sm_set_authentication_requirements(0); + + // le data channel setup + l2cap_le_register_service(&app_packet_handler, psm_x, LEVEL_0); + + // turn on! + hci_power_control(HCI_POWER_ON); + + return 0; +} + +/* EXAMPLE_END */ \ No newline at end of file From 8089d5b492dc23110850f52b0c6695de8e9d734a Mon Sep 17 00:00:00 2001 From: Matthias Ringwald Date: Fri, 8 Jul 2016 22:22:38 +0200 Subject: [PATCH 5/5] l2cap: start creating connection via gap whitelest --- src/l2cap.c | 118 ++++++++++++++++++++++++++++++++++++---------------- src/l2cap.h | 15 +++++-- 2 files changed, 92 insertions(+), 41 deletions(-) diff --git a/src/l2cap.c b/src/l2cap.c index a2cc9bfca..a209bb678 100644 --- a/src/l2cap.c +++ b/src/l2cap.c @@ -710,6 +710,16 @@ static void l2cap_run(void){ } #ifdef ENABLE_BLE + btstack_linked_list_iterator_init(&it, &l2cap_le_channels); + while (btstack_linked_list_iterator_has_next(&it)){ + l2cap_channel_t * channel = (l2cap_channel_t *) btstack_linked_list_iterator_next(&it); + // log_info("l2cap_run: channel %p, state %u, var 0x%02x", channel, channel->state, channel->state_var); + switch (channel->state){ + default: + break; + } + } + // send l2cap con paramter update if necessary hci_connections_get_iterator(&it); while(btstack_linked_list_iterator_has_next(&it)){ @@ -733,6 +743,16 @@ static void l2cap_run(void){ default: break; } +#if 0 +void l2cap_le_run(void){ + for all channels + switch(state): + + if outgoing transfer active + send next chunk + if done, notify app +} +#endif } #endif @@ -757,6 +777,18 @@ static void l2cap_handle_connection_complete(hci_con_handle_t con_handle, l2cap_ } } +#ifdef ENABLE_BLE +static void l2cap_handle_le_connection_complete(hci_con_handle_t con_handle, l2cap_channel_t * channel){ + if (channel->state == L2CAP_STATE_WAIT_CONNECTION_COMPLETE || channel->state == L2CAP_STATE_WILL_SEND_CREATE_CONNECTION) { + log_info("l2cap_le_handle_connection_complete expected state"); + // success, start l2cap handshake + channel->con_handle = con_handle; + // ... + } +} +#endif + + static void l2cap_handle_remote_supported_features_received(l2cap_channel_t * channel){ if (channel->state != L2CAP_STATE_WAIT_REMOTE_SUPPORTED_FEATURES) return; @@ -1589,10 +1621,7 @@ void l2cap_register_fixed_channel(btstack_packet_handler_t the_packet_handler, u static inline l2cap_service_t * l2cap_le_get_service(uint16_t psm){ return l2cap_get_service_internal(&l2cap_le_services, psm); } -/** - * @brief Regster L2CAP LE Credit Based Flow Control Mode service - * @param - */ + uint8_t l2cap_le_register_service(btstack_packet_handler_t packet_handler, uint16_t psm, gap_security_level_t security_level){ log_info("L2CAP_LE_REGISTER_SERVICE psm 0x%x", psm); @@ -1633,15 +1662,13 @@ uint8_t l2cap_le_unregister_service(uint16_t psm) { return 0; } -/* - * @brief Accept incoming LE Data Channel connection - * @param local_cid L2CAP LE Data Channel Identifier - * @param receive_buffer buffer used for reassembly of L2CAP LE Information Frames into service data unit (SDU) with given MTU - * @param receive_buffer_size buffer size equals MTU - * @param initial_credits Number of initial credits provided to peer - */ - uint8_t l2cap_le_accept_connection(uint16_t local_cid, uint8_t * receive_sdu_buffer, uint16_t mtu, uint16_t initial_credits){ + // get connection + // bail if missing + // set mtu and receive buffer + // check state + // set state accept connection + // l2cap_le_run() return 0; } @@ -1651,25 +1678,18 @@ uint8_t l2cap_le_accept_connection(uint16_t local_cid, uint8_t * receive_sdu_buf */ uint8_t l2cap_le_decline_connection(uint16_t local_cid){ + // get connection + // bail if missing + // check state + // set state decline connection + // l2cap_le_run() return 0; } -/** - * @brief Create LE Data Channel - * @param packet_handler Packet handler for this connection - * @param address Peer address - * @param address_type Peer address type - * @param psm Service PSM to connect to - * @param receive_buffer buffer used for reassembly of L2CAP LE Information Frames into service data unit (SDU) with given MTU - * @param receive_buffer_size buffer size equals MTU - * @param initial_credits Number of initial credits provided to peer - * @param security_level Minimum required security level - * @param out_local_cid L2CAP LE Channel Identifier is stored here - */ uint8_t l2cap_le_create_channel(btstack_packet_handler_t packet_handler, bd_addr_t address, bd_addr_type_t address_type, uint16_t psm, uint8_t * receive_sdu_buffer, uint16_t mtu, uint16_t initial_credits, gap_security_level_t security_level, - uint16_t * out_local_cid) -{ + uint16_t * out_local_cid) { + log_info("L2CAP_LE_CREATE_CHANNEL addr %s psm 0x%x mtu %u", bd_addr_to_str(address), psm, mtu); l2cap_channel_t * channel = l2cap_create_channel_entry(packet_handler, address, BD_ADDR_TYPE_CLASSIC, psm, mtu, LEVEL_0); @@ -1677,27 +1697,37 @@ uint8_t l2cap_le_create_channel(btstack_packet_handler_t packet_handler, bd_addr return BTSTACK_MEMORY_ALLOC_FAILED; } - // add to connections list - btstack_linked_list_add(&l2cap_le_channels, (btstack_linked_item_t *) channel); - // store local_cid if (out_local_cid){ *out_local_cid = channel->local_cid; } + // add to connections list + btstack_linked_list_add(&l2cap_le_channels, (btstack_linked_item_t *) channel); + // check if hci connection is already usable hci_connection_t * conn = hci_connection_for_bd_addr_and_type(address, address_type); + + channel->state = L2CAP_STATE_WAIT_CONNECTION_COMPLETE; + if (conn){ log_info("l2cap_le_create_channel, hci connection already exists"); - // l2cap_handle_connection_complete(conn->con_handle, channel); - // check if remote supported fearures are already received - // if (conn->bonding_flags & BONDING_RECEIVED_REMOTE_FEATURES) { - // l2cap_handle_remote_supported_features_received(channel); - // } + l2cap_handle_le_connection_complete(conn->con_handle, channel); + } else { + + // + // TODO: start timer + // .. + + // start connection + uint8_t res = gap_auto_connection_start(address_type, address); + if (!res){ + // discard channel object + btstack_linked_list_remove(&l2cap_channels, (btstack_linked_item_t *) channel); + btstack_memory_l2cap_channel_free(channel); + return res; + } } - - l2cap_run(); - return 0; } @@ -1707,6 +1737,12 @@ uint8_t l2cap_le_create_channel(btstack_packet_handler_t packet_handler, bd_addr * @param credits Number additional credits for peer */ uint8_t l2cap_le_provide_credits(uint16_t cid, uint16_t credits){ + // get connection + // bail if missing + // check state + // check incoming credits + credits <= 0xffff + // set credits_granted + // l2cap_le_run() return 0; } @@ -1715,6 +1751,7 @@ uint8_t l2cap_le_provide_credits(uint16_t cid, uint16_t credits){ * @param local_cid L2CAP LE Data Channel Identifier */ int l2cap_le_can_send_now(uint16_t cid){ + // check if data can be sent via le at all return 0; } @@ -1725,6 +1762,7 @@ int l2cap_le_can_send_now(uint16_t cid){ * @param local_cid L2CAP LE Data Channel Identifier */ uint8_t l2cap_le_request_can_send_now_event(uint16_t cid){ + // same as for non-le return 0; } @@ -1736,6 +1774,9 @@ uint8_t l2cap_le_request_can_send_now_event(uint16_t cid){ * @param size data size */ uint8_t l2cap_le_send_data(uint16_t cid, uint8_t * data, uint16_t size){ + // check if no outgoing data is pending + // store info + // l2cap_le_run() return 0; } @@ -1745,6 +1786,9 @@ uint8_t l2cap_le_send_data(uint16_t cid, uint8_t * data, uint16_t size){ */ uint8_t l2cap_le_disconnect(uint16_t cid) { + // find channel + // check state + // set state SEND_DISCONNECT return 0; } diff --git a/src/l2cap.h b/src/l2cap.h index abf570699..293d44e1f 100644 --- a/src/l2cap.h +++ b/src/l2cap.h @@ -126,7 +126,6 @@ typedef struct { uint16_t local_mtu; uint16_t remote_mtu; - uint16_t mps; // LE Data Channel uint16_t flush_timeout; // default 0xffff @@ -137,14 +136,22 @@ typedef struct { uint8_t reason; // used in decline internal uint8_t waiting_for_can_send_now; + // LE Data Channels + + // receive SDU buffer + uint8_t * receive_sdu_buffer; + + // max PDU size + uint16_t remote_mps; + // credits for outgoing traffic - uint8_t credits_outgoing; + uint16_t credits_outgoing; // number of packets remote will be granted - uint8_t new_credits_incoming; + uint16_t new_credits_incoming; // credits for incoming traffic - uint8_t credits_incoming; + uint16_t credits_incoming; } l2cap_channel_t;