hfp: sdp query

This commit is contained in:
Milanka Ringwald 2015-07-09 15:28:06 +02:00
parent 92fdd6fa5f
commit ea5029c9a4
12 changed files with 440 additions and 307 deletions

View File

@ -50,10 +50,6 @@
#include <stdlib.h>
#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));

View File

@ -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);

257
src/hfp.c Normal file
View File

@ -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 <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <btstack/hci_cmds.h>
#include <btstack/run_loop.h>
#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
}

93
src/hfp.h Normal file
View File

@ -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

View File

@ -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;
}
}

View File

@ -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();

View File

@ -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;
}
}

View File

@ -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();

View File

@ -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

View File

@ -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);

View File

@ -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!

View File

@ -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 <stdlib.h>
#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"