From ea5029c9a483b79f1566704b2cce99e9b13384b4 Mon Sep 17 00:00:00 2001 From: Milanka Ringwald Date: Thu, 9 Jul 2015 15:28:06 +0200 Subject: [PATCH] hfp: sdp query --- src/btstack_memory.c | 40 ++++- src/btstack_memory.h | 5 + src/hfp.c | 257 ++++++++++++++++++++++++++++++ src/hfp.h | 93 +++++++++++ src/hfp_ag.c | 148 +---------------- src/hfp_ag.h | 6 +- src/hfp_hf.c | 145 ++--------------- src/hfp_hf.h | 7 +- test/pts/Makefile | 4 +- test/pts/hfp_ag_test.c | 17 +- test/pts/hfp_hf_test.c | 18 ++- tools/btstack_memory_generator.py | 7 +- 12 files changed, 440 insertions(+), 307 deletions(-) create mode 100644 src/hfp.c create mode 100644 src/hfp.h diff --git a/src/btstack_memory.c b/src/btstack_memory.c index 74e806a15..2f0877f36 100644 --- a/src/btstack_memory.c +++ b/src/btstack_memory.c @@ -50,10 +50,6 @@ #include -#include "btstack-config.h" -#include "hci.h" -#include "l2cap.h" -#include "rfcomm.h" // MARK: hci_connection_t @@ -412,6 +408,39 @@ void btstack_memory_bnep_channel_free(bnep_channel_t *bnep_channel){ #endif + +// MARK: hfp_connection_t +#ifdef MAX_NO_HFP_CONNECTIONS +#if MAX_NO_HFP_CONNECTIONS > 0 +static hfp_connection_t hfp_connection_storage[MAX_NO_HFP_CONNECTIONS]; +static memory_pool_t hfp_connection_pool; +hfp_connection_t * btstack_memory_hfp_connection_get(void){ + return (hfp_connection_t *) memory_pool_get(&hfp_connection_pool); +} +void btstack_memory_hfp_connection_free(hfp_connection_t *hfp_connection){ + memory_pool_free(&hfp_connection_pool, hfp_connection); +} +#else +hfp_connection_t * btstack_memory_hfp_connection_get(void){ + return NULL; +} +void btstack_memory_hfp_connection_free(hfp_connection_t *hfp_connection){ + // silence compiler warning about unused parameter in a portable way + (void) hfp_connection; +}; +#endif +#elif defined(HAVE_MALLOC) +hfp_connection_t * btstack_memory_hfp_connection_get(void){ + return (hfp_connection_t*) malloc(sizeof(hfp_connection_t)); +} +void btstack_memory_hfp_connection_free(hfp_connection_t *hfp_connection){ + free(hfp_connection); +} +#else +#error "Neither HAVE_MALLOC nor MAX_NO_HFP_CONNECTIONS for struct hfp_connection is defined. Please, edit the config file." +#endif + + #ifdef HAVE_BLE // MARK: gatt_client_t @@ -514,6 +543,9 @@ void btstack_memory_init(void){ #if MAX_NO_BNEP_CHANNELS > 0 memory_pool_create(&bnep_channel_pool, bnep_channel_storage, MAX_NO_BNEP_CHANNELS, sizeof(bnep_channel_t)); #endif +#if MAX_NO_HFP_CONNECTIONS > 0 + memory_pool_create(&hfp_connection_pool, hfp_connection_storage, MAX_NO_HFP_CONNECTIONS, sizeof(hfp_connection_t)); +#endif #ifdef HAVE_BLE #if MAX_NO_GATT_CLIENTS > 0 memory_pool_create(&gatt_client_pool, gatt_client_storage, MAX_NO_GATT_CLIENTS, sizeof(gatt_client_t)); diff --git a/src/btstack_memory.h b/src/btstack_memory.h index 29318a643..349506261 100644 --- a/src/btstack_memory.h +++ b/src/btstack_memory.h @@ -57,6 +57,7 @@ extern "C" { #include "l2cap.h" #include "rfcomm.h" #include "bnep.h" +#include "hfp.h" #include "remote_device_db.h" #ifdef HAVE_BLE @@ -104,6 +105,10 @@ void btstack_memory_bnep_service_free(bnep_service_t *bnep_service); bnep_channel_t * btstack_memory_bnep_channel_get(void); void btstack_memory_bnep_channel_free(bnep_channel_t *bnep_channel); +// hfp_connection +hfp_connection_t * btstack_memory_hfp_connection_get(void); +void btstack_memory_hfp_connection_free(hfp_connection_t *hfp_connection); + #ifdef HAVE_BLE // gatt_client, gatt_subclient gatt_client_t * btstack_memory_gatt_client_get(void); diff --git a/src/hfp.c b/src/hfp.c new file mode 100644 index 000000000..d7176fd8d --- /dev/null +++ b/src/hfp.c @@ -0,0 +1,257 @@ +/* + * 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 + * + */ + +// ***************************************************************************** +// +// Minimal setup for HFP Audio Gateway (AG) unit (!! UNDER DEVELOPMENT !!) +// +// ***************************************************************************** + +#include "btstack-config.h" + +#include +#include +#include +#include + +#include +#include + +#include "hci.h" +#include "btstack_memory.h" +#include "hci_dump.h" +#include "l2cap.h" +#include "sdp_query_rfcomm.h" +#include "sdp.h" +#include "debug.h" +#include "hfp_ag.h" + +static hfp_callback_t hfp_callback; +static linked_list_t hfp_connections = NULL; + +static hfp_connection_t * get_hfp_connection_context_for_handle(uint16_t handle){ + linked_item_t *it; + for (it = (linked_item_t *) &hfp_connections; it ; it = it->next){ + hfp_connection_t * connection = (hfp_connection_t *) it; + if (connection->con_handle == handle){ + return connection; + } + } + return NULL; +} + +static hfp_connection_t * get_hfp_connection_context_for_bd_addr(bd_addr_t bd_addr){ + linked_item_t *it; + for (it = (linked_item_t *) &hfp_connections; it ; it = it->next){ + hfp_connection_t * connection = (hfp_connection_t *) it; + if (memcmp(connection->remote_addr, bd_addr, 6) == 0) { + return connection; + } + } + return NULL; +} + +static hfp_connection_t * create_hfp_connection_context(){ + hfp_connection_t * context = btstack_memory_hfp_connection_get(); + if (!context) return NULL; + // init state + context->state = HFP_IDLE; + linked_list_add(&hfp_connections, (linked_item_t*)context); + return context; +} + + +static hfp_connection_t * provide_hfp_connection_context_for_bd_addr(bd_addr_t bd_addr){ + hfp_connection_t * context = get_hfp_connection_context_for_bd_addr(bd_addr); + if (context) return context; + context = create_hfp_connection_context(); + memcpy(context->remote_addr, bd_addr, 6); + return context; +} + +hfp_connection_t * provide_hfp_connection_context_for_conn_handle(uint16_t con_handle){ + hfp_connection_t * context = get_hfp_connection_context_for_handle(con_handle); + if (context) return context; + context = create_hfp_connection_context(); + context->con_handle = con_handle; + return context; +} + +void hfp_register_packet_handler(hfp_callback_t callback){ + if (callback == NULL){ + log_error("hfp_register_packet_handler called with NULL callback"); + return; + } + hfp_callback = callback; +} + +void hfp_create_service(uint8_t * service, uint16_t service_uuid, int rfcomm_channel_nr, const char * name, uint16_t supported_features){ + uint8_t* attribute; + de_create_sequence(service); + + // 0x0000 "Service Record Handle" + de_add_number(service, DE_UINT, DE_SIZE_16, SDP_ServiceRecordHandle); + de_add_number(service, DE_UINT, DE_SIZE_32, 0x10001); + + // 0x0001 "Service Class ID List" + de_add_number(service, DE_UINT, DE_SIZE_16, SDP_ServiceClassIDList); + attribute = de_push_sequence(service); + { + // "UUID for Service" + de_add_number(attribute, DE_UUID, DE_SIZE_16, service_uuid); + de_add_number(attribute, DE_UUID, DE_SIZE_16, SDP_GenericAudio); + } + de_pop_sequence(service, attribute); + + // 0x0004 "Protocol Descriptor List" + de_add_number(service, DE_UINT, DE_SIZE_16, SDP_ProtocolDescriptorList); + attribute = de_push_sequence(service); + { + uint8_t* l2cpProtocol = de_push_sequence(attribute); + { + de_add_number(l2cpProtocol, DE_UUID, DE_SIZE_16, SDP_L2CAPProtocol); + } + de_pop_sequence(attribute, l2cpProtocol); + + uint8_t* rfcomm = de_push_sequence(attribute); + { + de_add_number(rfcomm, DE_UUID, DE_SIZE_16, SDP_RFCOMMProtocol); // 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, SDP_BrowseGroupList); // public browse group + attribute = de_push_sequence(service); + { + de_add_number(attribute, DE_UUID, DE_SIZE_16, SDP_PublicBrowseGroup); + } + de_pop_sequence(service, attribute); + + // 0x0009 "Bluetooth Profile Descriptor List" + de_add_number(service, DE_UINT, DE_SIZE_16, SDP_BluetoothProfileDescriptorList); + attribute = de_push_sequence(service); + { + uint8_t *sppProfile = de_push_sequence(attribute); + { + de_add_number(sppProfile, DE_UUID, DE_SIZE_16, SDP_Handsfree); + de_add_number(sppProfile, DE_UINT, DE_SIZE_16, 0x0107); // Verision 1.7 + } + de_pop_sequence(attribute, sppProfile); + } + de_pop_sequence(service, attribute); + + // 0x0100 "Service Name" + de_add_number(service, DE_UINT, DE_SIZE_16, 0x0100); + de_add_data(service, DE_STRING, strlen(name), (uint8_t *) name); + + de_add_number(service, DE_UINT, DE_SIZE_16, supported_features); + /* Bit position: + * 0: EC and/or NR function (yes/no, 1 = yes, 0 = no) + * 1: Call waiting or three-way calling(yes/no, 1 = yes, 0 = no) + * 2: CLI presentation capability (yes/no, 1 = yes, 0 = no) + * 3: Voice recognition activation (yes/no, 1= yes, 0 = no) + * 4: Remote volume control (yes/no, 1 = yes, 0 = no) + * 5: Wide band speech (yes/no, 1 = yes, 0 = no) + */ +} + +static hfp_connection_t * connection_doing_sdp_query = NULL; +static void handle_query_rfcomm_event(sdp_query_event_t * event, void * context){ + sdp_query_rfcomm_service_event_t * ve; + sdp_query_complete_event_t * ce; + hfp_connection_t * connection = connection_doing_sdp_query; + + switch (event->type){ + case SDP_QUERY_RFCOMM_SERVICE: + ve = (sdp_query_rfcomm_service_event_t*) event; + if (!connection) { + log_error("handle_query_rfcomm_event alloc connection for RFCOMM port %u failed", ve->channel_nr); + return; + } + connection->rfcomm_channel_nr = ve->channel_nr; + break; + case SDP_QUERY_COMPLETE: + connection_doing_sdp_query = NULL; + ce = (sdp_query_complete_event_t*) event; + + if (connection->rfcomm_channel_nr > 0){ + connection->state = HFP_W4_RFCOMM_CONNECTED; + rfcomm_create_channel_internal(NULL, connection->remote_addr, connection->rfcomm_channel_nr); + break; + } + log_info("rfcomm service not found, status %u.", ce->status); + break; + default: + break; + } +} + +static void packet_handler (void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ + // printf("packet_handler type %u, packet[0] %x\n", packet_type, packet[0]); + if (packet_type == RFCOMM_DATA_PACKET){ + } + + if (packet_type != HCI_EVENT_PACKET) return; +} + + +void hfp_init(uint16_t rfcomm_channel_nr){ + rfcomm_register_service_internal(NULL, rfcomm_channel_nr, 0xffff); + + l2cap_register_packet_handler(packet_handler); + rfcomm_register_packet_handler(packet_handler); + + sdp_query_rfcomm_register_callback(handle_query_rfcomm_event, NULL); +} + +void hfp_connect(bd_addr_t bd_addr){ + hfp_connection_t * connection = provide_hfp_connection_context_for_bd_addr(bd_addr); + if (!connection) { + log_error("hfp_hf_connect for addr %s failed", bd_addr_to_str(bd_addr)); + return; + } + + if (connection->state != HFP_IDLE) return; + connection->state = HFP_SDP_QUERY_RFCOMM_CHANNEL; + memcpy(connection->remote_addr, bd_addr, 6); + connection_doing_sdp_query = connection; + // @TODO trigger SDP query +} diff --git a/src/hfp.h b/src/hfp.h new file mode 100644 index 000000000..34336540f --- /dev/null +++ b/src/hfp.h @@ -0,0 +1,93 @@ +/* + * 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 + * + */ + +// ***************************************************************************** +// +// HFP Hands-Free (HF) unit and Audio-Gateway Commons +// +// ***************************************************************************** + + +#ifndef btstack_hfp_h +#define btstack_hfp_h + +#include "hci.h" +#include "sdp_query_rfcomm.h" + +#if defined __cplusplus +extern "C" { +#endif + +typedef enum { + HFP_IDLE, + HFP_SDP_QUERY_RFCOMM_CHANNEL, + HFP_W4_SDP_QUERY_COMPLETE, + HFP_W4_RFCOMM_CONNECTED, + HFP_ACTIVE, + HFP_W2_DISCONNECT_RFCOMM, + HFP_W4_RFCOMM_DISCONNECTED, + HFP_W4_CONNECTION_ESTABLISHED_TO_SHUTDOWN +} hfp_state_t; + +typedef void (*hfp_callback_t)(uint8_t * event, uint16_t event_size); + +typedef struct hfp_connection { + linked_item_t item; + hfp_state_t state; + + bd_addr_t remote_addr; + uint16_t con_handle; + uint16_t rfcomm_channel_nr; + uint16_t rfcomm_cid; + + hfp_callback_t callback; +} hfp_connection_t; + + +void hfp_create_service(uint8_t * service, uint16_t service_uuid, int rfcomm_channel_nr, const char * name, uint16_t supported_features); + +void hfp_init(uint16_t rfcomm_channel_nr); +void hfp_register_packet_handler(hfp_callback_t callback); + +hfp_connection_t * provide_hfp_connection_context_for_conn_handle(uint16_t con_handle); +void hfp_connect(bd_addr_t bd_addr); + +#if defined __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/src/hfp_ag.c b/src/hfp_ag.c index 12ae1b349..f56efe6d4 100644 --- a/src/hfp_ag.c +++ b/src/hfp_ag.c @@ -58,106 +58,18 @@ #include "sdp_query_rfcomm.h" #include "sdp.h" #include "debug.h" +#include "hfp.h" #include "hfp_ag.h" -typedef enum { - HFP_IDLE, - HFP_SDP_QUERY_RFCOMM_CHANNEL, - HFP_W4_SDP_QUERY_COMPLETE, - HFP_W4_RFCOMM_CONNECTED, - HFP_ACTIVE, - HFP_W2_DISCONNECT_RFCOMM, - HFP_W4_RFCOMM_DISCONNECTED, - HFP_W4_CONNECTION_ESTABLISHED_TO_SHUTDOWN -} hfp_state_t; - -static hfp_state_t hfp_state = HFP_IDLE; - static const char default_hfp_ag_service_name[] = "Voice gateway"; -static bd_addr_t remote; -static uint8_t channel_nr = 0; - -static hfp_ag_callback_t hfp_ag_callback; static void hfp_run(); -static void packet_handler (void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); -static void handle_query_rfcomm_event(sdp_query_event_t * event, void * context); - -static void dummy_notify(uint8_t * event, uint16_t size){} - -void hfp_ag_register_packet_handler(hfp_ag_callback_t callback){ - if (callback == NULL){ - callback = &dummy_notify; - } - hfp_ag_callback = callback; -} void hfp_ag_create_service(uint8_t * service, int rfcomm_channel_nr, const char * name, uint8_t ability_to_reject_call, uint16_t supported_features){ - uint8_t* attribute; - de_create_sequence(service); - - // 0x0000 "Service Record Handle" - de_add_number(service, DE_UINT, DE_SIZE_16, SDP_ServiceRecordHandle); - de_add_number(service, DE_UINT, DE_SIZE_32, 0x10001); - - // 0x0001 "Service Class ID List" - de_add_number(service, DE_UINT, DE_SIZE_16, SDP_ServiceClassIDList); - attribute = de_push_sequence(service); - { - // "UUID for PAN Service" - de_add_number(attribute, DE_UUID, DE_SIZE_16, SDP_HandsfreeAudioGateway); - de_add_number(attribute, DE_UUID, DE_SIZE_16, SDP_GenericAudio); - } - de_pop_sequence(service, attribute); - - // 0x0004 "Protocol Descriptor List" - de_add_number(service, DE_UINT, DE_SIZE_16, SDP_ProtocolDescriptorList); - attribute = de_push_sequence(service); - { - uint8_t* l2cpProtocol = de_push_sequence(attribute); - { - de_add_number(l2cpProtocol, DE_UUID, DE_SIZE_16, SDP_L2CAPProtocol); - } - de_pop_sequence(attribute, l2cpProtocol); - - uint8_t* rfcomm = de_push_sequence(attribute); - { - de_add_number(rfcomm, DE_UUID, DE_SIZE_16, SDP_RFCOMMProtocol); // 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, SDP_BrowseGroupList); // public browse group - attribute = de_push_sequence(service); - { - de_add_number(attribute, DE_UUID, DE_SIZE_16, SDP_PublicBrowseGroup); - } - de_pop_sequence(service, attribute); - - // 0x0009 "Bluetooth Profile Descriptor List" - de_add_number(service, DE_UINT, DE_SIZE_16, SDP_BluetoothProfileDescriptorList); - attribute = de_push_sequence(service); - { - uint8_t *sppProfile = de_push_sequence(attribute); - { - de_add_number(sppProfile, DE_UUID, DE_SIZE_16, SDP_Handsfree); - de_add_number(sppProfile, DE_UINT, DE_SIZE_16, 0x0107); // Verision 1.7 - } - de_pop_sequence(attribute, sppProfile); - } - de_pop_sequence(service, attribute); - - // 0x0100 "Service Name" - de_add_number(service, DE_UINT, DE_SIZE_16, 0x0100); - if (name){ - de_add_data(service, DE_STRING, strlen(name), (uint8_t *) name); - } else { - de_add_data(service, DE_STRING, strlen(default_hfp_ag_service_name), (uint8_t *) default_hfp_ag_service_name); + if (!name){ + name = default_hfp_ag_service_name; } + hfp_create_service(service, SDP_HandsfreeAudioGateway, rfcomm_channel_nr, name, supported_features); // Network de_add_number(service, DE_UINT, DE_SIZE_8, ability_to_reject_call); @@ -165,26 +77,13 @@ void hfp_ag_create_service(uint8_t * service, int rfcomm_channel_nr, const char * 0x01 – Ability to reject a call * 0x00 – No ability to reject a call */ - - // SupportedFeatures - de_add_number(service, DE_UINT, DE_SIZE_16, supported_features); - /* Bit position: - * 0: EC and/or NR function (yes/no, 1 = yes, 0 = no) - * 1: Call waiting or three-way calling(yes/no, 1 = yes, 0 = no) - * 2: CLI presentation capability (yes/no, 1 = yes, 0 = no) - * 3: Voice recognition activation (yes/no, 1= yes, 0 = no) - * 4: Remote volume control (yes/no, 1 = yes, 0 = no) - * 5: Wide band speech (yes/no, 1 = yes, 0 = no) - */ -} - -void hfp_ag_init(uint8_t rfcomm_channel_nr){ - hfp_run(); } void hfp_ag_connect(bd_addr_t bd_addr){ + hfp_connect(bd_addr); hfp_run(); } + void hfp_ag_disconnect(){ hfp_run(); } @@ -192,38 +91,3 @@ void hfp_ag_disconnect(){ static void hfp_run(void){ } -static void packet_handler (void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ - // printf("packet_handler type %u, packet[0] %x\n", packet_type, packet[0]); - if (packet_type == RFCOMM_DATA_PACKET){ - } - - if (packet_type != HCI_EVENT_PACKET) return; -} - -static void handle_query_rfcomm_event(sdp_query_event_t * event, void * context){ - sdp_query_rfcomm_service_event_t * ve; - sdp_query_complete_event_t * ce; - - switch (event->type){ - case SDP_QUERY_RFCOMM_SERVICE: - ve = (sdp_query_rfcomm_service_event_t*) event; - channel_nr = ve->channel_nr; - printf("** Service name: '%s', RFCOMM port %u\n", ve->service_name, channel_nr); - break; - case SDP_QUERY_COMPLETE: - ce = (sdp_query_complete_event_t*) event; - - if (channel_nr > 0){ - hfp_state = HFP_W4_RFCOMM_CONNECTED; - printf("RFCOMM create channel. state %d\n", HFP_W4_RFCOMM_CONNECTED); - rfcomm_create_channel_internal(NULL, remote, channel_nr); - break; - } - // hfp_ag_reset_state(); - printf("Service not found, status %u.\n", ce->status); - break; - default: - break; - } -} - diff --git a/src/hfp_ag.h b/src/hfp_ag.h index fb5a6fd0d..3598d278f 100644 --- a/src/hfp_ag.h +++ b/src/hfp_ag.h @@ -47,17 +47,13 @@ #include "hci.h" #include "sdp_query_rfcomm.h" +#include "hfp.h" #if defined __cplusplus extern "C" { #endif -typedef void (*hfp_ag_callback_t)(uint8_t * event, uint16_t event_size); -// Register callback (packet handler) for hsp headset -void hfp_ag_register_packet_handler(hfp_ag_callback_t callback); void hfp_ag_create_service(uint8_t * service, int rfcomm_channel_nr, const char * name, uint8_t ability_to_reject_call, uint16_t supported_features); - -void hfp_ag_init(uint8_t rfcomm_channel_nr); void hfp_ag_connect(bd_addr_t bd_addr); void hfp_ag_disconnect(); diff --git a/src/hfp_hf.c b/src/hfp_hf.c index d6ee26806..8a299fc3b 100644 --- a/src/hfp_hf.c +++ b/src/hfp_hf.c @@ -58,126 +58,37 @@ #include "sdp_query_rfcomm.h" #include "sdp.h" #include "debug.h" +#include "hfp.h" #include "hfp_hf.h" -typedef enum { - HFP_IDLE, - HFP_SDP_QUERY_RFCOMM_CHANNEL, - HFP_W4_SDP_QUERY_COMPLETE, - HFP_W4_RFCOMM_CONNECTED, - HFP_ACTIVE, - HFP_W2_DISCONNECT_RFCOMM, - HFP_W4_RFCOMM_DISCONNECTED, - HFP_W4_CONNECTION_ESTABLISHED_TO_SHUTDOWN -} hfp_state_t; - -static hfp_state_t hfp_state = HFP_IDLE; - - static const char default_hfp_hf_service_name[] = "Hands-Free unit"; -static bd_addr_t remote; -static uint8_t channel_nr = 0; -static hfp_hf_callback_t hfp_hf_callback; static void hfp_run(); static void packet_handler (void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); -static void handle_query_rfcomm_event(sdp_query_event_t * event, void * context); - -static void dummy_notify(uint8_t * event, uint16_t size){} - -void hfp_hf_register_packet_handler(hfp_hf_callback_t callback){ - if (callback == NULL){ - callback = &dummy_notify; - } - hfp_hf_callback = callback; -} void hfp_hf_create_service(uint8_t * service, int rfcomm_channel_nr, const char * name, uint16_t supported_features){ - uint8_t* attribute; - de_create_sequence(service); - - // 0x0000 "Service Record Handle" - de_add_number(service, DE_UINT, DE_SIZE_16, SDP_ServiceRecordHandle); - de_add_number(service, DE_UINT, DE_SIZE_32, 0x10001); - - // 0x0001 "Service Class ID List" - de_add_number(service, DE_UINT, DE_SIZE_16, SDP_ServiceClassIDList); - attribute = de_push_sequence(service); - { - // "UUID for PAN Service" - de_add_number(attribute, DE_UUID, DE_SIZE_16, SDP_Handsfree); - de_add_number(attribute, DE_UUID, DE_SIZE_16, SDP_GenericAudio); + if (!name){ + name = default_hfp_hf_service_name; } - de_pop_sequence(service, attribute); - - // 0x0004 "Protocol Descriptor List" - de_add_number(service, DE_UINT, DE_SIZE_16, SDP_ProtocolDescriptorList); - attribute = de_push_sequence(service); - { - uint8_t* l2cpProtocol = de_push_sequence(attribute); - { - de_add_number(l2cpProtocol, DE_UUID, DE_SIZE_16, SDP_L2CAPProtocol); - } - de_pop_sequence(attribute, l2cpProtocol); - - uint8_t* rfcomm = de_push_sequence(attribute); - { - de_add_number(rfcomm, DE_UUID, DE_SIZE_16, SDP_RFCOMMProtocol); // 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, SDP_BrowseGroupList); // public browse group - attribute = de_push_sequence(service); - { - de_add_number(attribute, DE_UUID, DE_SIZE_16, SDP_PublicBrowseGroup); - } - de_pop_sequence(service, attribute); - - // 0x0009 "Bluetooth Profile Descriptor List" - de_add_number(service, DE_UINT, DE_SIZE_16, SDP_BluetoothProfileDescriptorList); - attribute = de_push_sequence(service); - { - uint8_t *sppProfile = de_push_sequence(attribute); - { - de_add_number(sppProfile, DE_UUID, DE_SIZE_16, SDP_Handsfree); - de_add_number(sppProfile, DE_UINT, DE_SIZE_16, 0x0107); // Verision 1.7 - } - de_pop_sequence(attribute, sppProfile); - } - de_pop_sequence(service, attribute); - - // 0x0100 "Service Name" - de_add_number(service, DE_UINT, DE_SIZE_16, 0x0100); - if (name){ - de_add_data(service, DE_STRING, strlen(name), (uint8_t *) name); - } else { - de_add_data(service, DE_STRING, strlen(default_hfp_hf_service_name), (uint8_t *) default_hfp_hf_service_name); - } - - de_add_number(service, DE_UINT, DE_SIZE_16, supported_features); - /* Bit position: - * 0: EC and/or NR function (yes/no, 1 = yes, 0 = no) - * 1: Call waiting or three-way calling(yes/no, 1 = yes, 0 = no) - * 2: CLI presentation capability (yes/no, 1 = yes, 0 = no) - * 3: Voice recognition activation (yes/no, 1= yes, 0 = no) - * 4: Remote volume control (yes/no, 1 = yes, 0 = no) - * 5: Wide band speech (yes/no, 1 = yes, 0 = no) - */ + hfp_create_service(service, SDP_Handsfree, rfcomm_channel_nr, name, supported_features); } -void hfp_hf_init(uint8_t rfcomm_channel_nr){ - hfp_run(); -} + +// static void hfp_hf_reset_state(uint16_t con_handle){ +// hfp_connection_t * connection = provide_hfp_connection_context_for_conn_handle(con_handle); +// if (!connection) { +// log_error("hfp_hf_reset_state for handle 0x%02x failed", con_handle); +// return; +// } +// connection->state = HFP_IDLE; +// } void hfp_hf_connect(bd_addr_t bd_addr){ + hfp_connect(bd_addr); hfp_run(); } + void hfp_hf_disconnect(){ hfp_run(); } @@ -193,29 +104,3 @@ static void packet_handler (void * connection, uint8_t packet_type, uint16_t cha if (packet_type != HCI_EVENT_PACKET) return; } -static void handle_query_rfcomm_event(sdp_query_event_t * event, void * context){ - sdp_query_rfcomm_service_event_t * ve; - sdp_query_complete_event_t * ce; - - switch (event->type){ - case SDP_QUERY_RFCOMM_SERVICE: - ve = (sdp_query_rfcomm_service_event_t*) event; - channel_nr = ve->channel_nr; - printf("** Service name: '%s', RFCOMM port %u\n", ve->service_name, channel_nr); - break; - case SDP_QUERY_COMPLETE: - ce = (sdp_query_complete_event_t*) event; - - if (channel_nr > 0){ - hfp_state = HFP_W4_RFCOMM_CONNECTED; - printf("RFCOMM create channel. state %d\n", HFP_W4_RFCOMM_CONNECTED); - rfcomm_create_channel_internal(NULL, remote, channel_nr); - break; - } - // hfp_ag_reset_state(); - printf("Service not found, status %u.\n", ce->status); - break; - default: - break; - } -} diff --git a/src/hfp_hf.h b/src/hfp_hf.h index efd20da6d..3d28ced35 100644 --- a/src/hfp_hf.h +++ b/src/hfp_hf.h @@ -47,17 +47,14 @@ #include "hci.h" #include "sdp_query_rfcomm.h" +#include "hfp.h" #if defined __cplusplus extern "C" { #endif -typedef void (*hfp_hf_callback_t)(uint8_t * event, uint16_t event_size); -// Register callback (packet handler) for hsp headset -void hfp_hf_register_packet_handler(hfp_hf_callback_t callback); -void hfp_hf_create_service(uint8_t * service, int rfcomm_channel_nr, const char * name, uint16_t supported_features); -void hfp_hf_init(uint8_t rfcomm_channel_nr); +void hfp_hf_create_service(uint8_t * service, int rfcomm_channel_nr, const char * name, uint16_t supported_features); void hfp_hf_connect(bd_addr_t bd_addr); void hfp_hf_disconnect(); diff --git a/test/pts/Makefile b/test/pts/Makefile index 794e6985d..d94687a7b 100644 --- a/test/pts/Makefile +++ b/test/pts/Makefile @@ -37,10 +37,10 @@ hsp_ag_test: ${CORE_OBJ} ${COMMON_OBJ} ${SDP_CLIENT} hsp_ag.o hsp_ag_test.c hsp_hs_test: ${CORE_OBJ} ${COMMON_OBJ} ${SDP_CLIENT} hsp_hs.o hsp_hs_test.c ${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@ -hfp_hf_test: ${CORE_OBJ} ${COMMON_OBJ} ${SDP_CLIENT} hfp_hf.o hfp_hf_test.c +hfp_hf_test: ${CORE_OBJ} ${COMMON_OBJ} ${SDP_CLIENT} hfp.o hfp_hf.o hfp_hf_test.c ${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@ -hfp_ag_test: ${CORE_OBJ} ${COMMON_OBJ} ${SDP_CLIENT} hfp_ag.o hfp_ag_test.c +hfp_ag_test: ${CORE_OBJ} ${COMMON_OBJ} ${SDP_CLIENT} hfp.o hfp_ag.o hfp_ag_test.c ${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@ l2cap_test: ${CORE_OBJ} ${COMMON_OBJ} l2cap_test.c diff --git a/test/pts/hfp_ag_test.c b/test/pts/hfp_ag_test.c index b5fb5591d..7e8a8066b 100644 --- a/test/pts/hfp_ag_test.c +++ b/test/pts/hfp_ag_test.c @@ -62,6 +62,7 @@ #include "hci.h" #include "l2cap.h" +#include "rfcomm.h" #include "sdp.h" #include "debug.h" #include "hfp_ag.h" @@ -74,7 +75,6 @@ const char hfp_ag_service_name[] = "Headset Test"; static bd_addr_t pts_addr = {0x00,0x1b,0xDC,0x07,0x32,0xEF}; static bd_addr_t local_mac = {0x04, 0x0C, 0xCE, 0xE4, 0x85, 0xD3}; -static char hs_cmd_buffer[100]; // prototypes static void show_usage(); @@ -126,16 +126,21 @@ void packet_handler(uint8_t * event, uint16_t event_size){ } int btstack_main(int argc, const char * argv[]){ + // init L2CAP + l2cap_init(); + rfcomm_init(); + + hfp_init(rfcomm_channel_nr); + hfp_register_packet_handler(packet_handler); + + sdp_init(); + // init SDP, create record for SPP and register with SDP memset((uint8_t *)hfp_service_buffer, 0, sizeof(hfp_service_buffer)); hfp_ag_create_service((uint8_t *)hfp_service_buffer, rfcomm_channel_nr, hfp_ag_service_name, 0, 0); - - hfp_ag_init(rfcomm_channel_nr); - hfp_ag_register_packet_handler(packet_handler); - - sdp_init(); sdp_register_service_internal(NULL, (uint8_t *)hfp_service_buffer); + // turn on! hci_power_control(HCI_POWER_ON); diff --git a/test/pts/hfp_hf_test.c b/test/pts/hfp_hf_test.c index 447cfbd08..ff838e006 100644 --- a/test/pts/hfp_hf_test.c +++ b/test/pts/hfp_hf_test.c @@ -62,6 +62,7 @@ #include "hci.h" #include "l2cap.h" +#include "rfcomm.h" #include "sdp.h" #include "debug.h" #include "hfp_hf.h" @@ -73,9 +74,7 @@ const char hfp_hf_service_name[] = "Headset Test"; static bd_addr_t pts_addr = {0x00,0x1b,0xDC,0x07,0x32,0xEF}; static bd_addr_t local_mac = {0x04, 0x0C, 0xCE, 0xE4, 0x85, 0xD3}; - -static char hs_cmd_buffer[100]; - + // prototypes static void show_usage(); @@ -126,14 +125,17 @@ void packet_handler(uint8_t * event, uint16_t event_size){ } int btstack_main(int argc, const char * argv[]){ + // init L2CAP + l2cap_init(); + rfcomm_init(); + + hfp_init(rfcomm_channel_nr); + hfp_register_packet_handler(packet_handler); + + sdp_init(); // init SDP, create record for SPP and register with SDP memset((uint8_t *)hfp_service_buffer, 0, sizeof(hfp_service_buffer)); hfp_hf_create_service((uint8_t *)hfp_service_buffer, rfcomm_channel_nr, hfp_hf_service_name, 0); - - hfp_hf_init(rfcomm_channel_nr); - hfp_hf_register_packet_handler(packet_handler); - - sdp_init(); sdp_register_service_internal(NULL, (uint8_t *)hfp_service_buffer); // turn on! diff --git a/tools/btstack_memory_generator.py b/tools/btstack_memory_generator.py index 12b73adf3..79ffdd1ef 100755 --- a/tools/btstack_memory_generator.py +++ b/tools/btstack_memory_generator.py @@ -60,6 +60,7 @@ extern "C" { #include "l2cap.h" #include "rfcomm.h" #include "bnep.h" +#include "hfp.h" #include "remote_device_db.h" #ifdef HAVE_BLE @@ -99,10 +100,6 @@ cfile_header_begin = """ #include -#include "btstack-config.h" -#include "hci.h" -#include "l2cap.h" -#include "rfcomm.h" """ header_template = """STRUCT_NAME_t * btstack_memory_STRUCT_NAME_get(void); @@ -155,7 +152,7 @@ def replacePlaceholder(template, struct_name): snippet = template.replace("STRUCT_TYPE", struct_type).replace("STRUCT_NAME", struct_name).replace("POOL_COUNT", pool_count) return snippet -list_of_structs = [ ["hci_connection"], ["l2cap_service", "l2cap_channel"], ["rfcomm_multiplexer", "rfcomm_service", "rfcomm_channel"], ["db_mem_device_name", "db_mem_device_link_key", "db_mem_service"], ["bnep_service", "bnep_channel"]] +list_of_structs = [ ["hci_connection"], ["l2cap_service", "l2cap_channel"], ["rfcomm_multiplexer", "rfcomm_service", "rfcomm_channel"], ["db_mem_device_name", "db_mem_device_link_key", "db_mem_service"], ["bnep_service", "bnep_channel"], ["hfp_connection"]] list_of_le_structs = [["gatt_client", "gatt_subclient"]] file_name = "../src/btstack_memory"