2015-07-09 15:28:06 +02:00
/*
* 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 !!)
//
// *****************************************************************************
2016-01-21 15:41:16 +01:00
# include "btstack_config.h"
2015-07-09 15:28:06 +02:00
# include <stdint.h>
# include <stdio.h>
# include <stdlib.h>
# include <string.h>
2015-08-07 21:45:00 +02:00
# include <inttypes.h>
2015-07-09 15:28:06 +02:00
2016-01-21 11:33:17 +01:00
# include "hci_cmd.h"
2016-01-20 15:08:39 +01:00
# include "btstack_run_loop.h"
2015-07-09 15:28:06 +02:00
# include "hci.h"
# include "btstack_memory.h"
# include "hci_dump.h"
# include "l2cap.h"
2015-11-13 15:04:41 +01:00
# include "classic/sdp_query_rfcomm.h"
# include "classic/sdp.h"
2016-01-20 14:52:45 +01:00
# include "btstack_debug.h"
2016-01-29 18:06:32 +01:00
# include "btstack_event.h"
2015-07-09 15:28:06 +02:00
2015-07-17 11:14:34 +02:00
# define HFP_HF_FEATURES_SIZE 10
# define HFP_AG_FEATURES_SIZE 12
2015-08-13 20:55:21 +02:00
2015-07-17 11:14:34 +02:00
static const char * hfp_hf_features [ ] = {
" EC and/or NR function " ,
" Three-way calling " ,
" CLI presentation capability " ,
" Voice recognition activation " ,
" Remote volume control " ,
2015-08-13 20:55:21 +02:00
2015-07-17 11:14:34 +02:00
" Enhanced call status " ,
" Enhanced call control " ,
2015-08-13 20:55:21 +02:00
2015-07-17 11:14:34 +02:00
" Codec negotiation " ,
2015-08-13 20:55:21 +02:00
2015-07-17 11:14:34 +02:00
" HF Indicators " ,
" eSCO S4 (and T2) Settings Supported " ,
" Reserved for future definition "
} ;
static const char * hfp_ag_features [ ] = {
" Three-way calling " ,
" EC and/or NR function " ,
" Voice recognition function " ,
" In-band ring tone capability " ,
" Attach a number to a voice tag " ,
" Ability to reject a call " ,
" Enhanced call status " ,
" Enhanced call control " ,
" Extended Error Result Codes " ,
" Codec negotiation " ,
" HF Indicators " ,
" eSCO S4 (and T2) Settings Supported " ,
" Reserved for future definition "
} ;
2015-08-12 14:14:55 +02:00
static int hfp_generic_status_indicators_nr = 0 ;
2015-08-13 00:42:42 +02:00
static hfp_generic_status_indicator_t hfp_generic_status_indicators [ HFP_MAX_NUM_HF_INDICATORS ] ;
2015-08-05 17:05:16 +02:00
2016-01-20 15:38:37 +01:00
static btstack_linked_list_t hfp_connections = NULL ;
2015-11-26 22:05:31 +01:00
static void parse_sequence ( hfp_connection_t * context ) ;
2015-07-09 17:03:16 +02:00
2015-11-06 21:34:45 +01:00
hfp_generic_status_indicator_t * get_hfp_generic_status_indicators ( void ) {
2015-08-13 00:42:42 +02:00
return ( hfp_generic_status_indicator_t * ) & hfp_generic_status_indicators ;
2015-08-12 12:22:53 +02:00
}
2015-11-06 21:34:45 +01:00
int get_hfp_generic_status_indicators_nr ( void ) {
2015-08-12 14:14:55 +02:00
return hfp_generic_status_indicators_nr ;
2015-08-12 12:22:53 +02:00
}
2015-08-13 00:42:42 +02:00
void set_hfp_generic_status_indicators ( hfp_generic_status_indicator_t * indicators , int indicator_nr ) {
2015-08-12 12:22:53 +02:00
if ( indicator_nr > HFP_MAX_NUM_HF_INDICATORS ) return ;
2015-08-12 14:14:55 +02:00
hfp_generic_status_indicators_nr = indicator_nr ;
2015-08-13 00:42:42 +02:00
memcpy ( hfp_generic_status_indicators , indicators , indicator_nr * sizeof ( hfp_generic_status_indicator_t ) ) ;
2015-08-12 12:22:53 +02:00
}
2015-07-17 11:14:34 +02:00
const char * hfp_hf_feature ( int index ) {
if ( index > HFP_HF_FEATURES_SIZE ) {
return hfp_hf_features [ HFP_HF_FEATURES_SIZE ] ;
}
return hfp_hf_features [ index ] ;
}
const char * hfp_ag_feature ( int index ) {
if ( index > HFP_AG_FEATURES_SIZE ) {
return hfp_ag_features [ HFP_AG_FEATURES_SIZE ] ;
}
return hfp_ag_features [ index ] ;
}
2015-07-10 17:16:25 +02:00
int send_str_over_rfcomm ( uint16_t cid , char * command ) {
2015-08-27 17:09:44 +02:00
if ( ! rfcomm_can_send_packet_now ( cid ) ) return 1 ;
2016-01-07 14:09:31 +01:00
log_info ( " HFP_TX %s " , command ) ;
2016-01-21 12:09:19 +01:00
int err = rfcomm_send ( cid , ( uint8_t * ) command , strlen ( command ) ) ;
2015-07-10 17:16:25 +02:00
if ( err ) {
2016-01-21 12:09:19 +01:00
log_error ( " rfcomm_send -> error 0x%02x \n " , err ) ;
2015-08-07 21:45:00 +02:00
}
2015-10-15 13:55:28 +02:00
return 1 ;
2015-07-10 17:16:25 +02:00
}
2015-11-06 21:34:45 +01:00
#if 0
2015-08-06 10:22:02 +02:00
void hfp_set_codec ( hfp_connection_t * context , uint8_t * packet , uint16_t size ) {
// parse available codecs
int pos = 0 ;
int i ;
for ( i = 0 ; i < size ; i + + ) {
pos + = 8 ;
if ( packet [ pos ] > context - > negotiated_codec ) {
context - > negotiated_codec = packet [ pos ] ;
}
}
printf ( " Negotiated Codec 0x%02x \n " , context - > negotiated_codec ) ;
}
2015-11-06 21:34:45 +01:00
# endif
2015-08-06 10:22:02 +02:00
// UTILS
int get_bit ( uint16_t bitmap , int position ) {
return ( bitmap > > position ) & 1 ;
}
int store_bit ( uint32_t bitmap , int position , uint8_t value ) {
if ( value ) {
bitmap | = 1 < < position ;
} else {
bitmap & = ~ ( 1 < < position ) ;
}
return bitmap ;
}
2015-07-30 13:16:53 +02:00
int join ( char * buffer , int buffer_size , uint8_t * values , int values_nr ) {
if ( buffer_size < values_nr * 3 ) return 0 ;
2015-07-17 14:58:57 +02:00
int i ;
int offset = 0 ;
for ( i = 0 ; i < values_nr - 1 ; i + + ) {
offset + = snprintf ( buffer + offset , buffer_size - offset , " %d, " , values [ i ] ) ; // puts string into buffer
2015-07-10 17:16:25 +02:00
}
2015-07-17 14:58:57 +02:00
if ( i < values_nr ) {
offset + = snprintf ( buffer + offset , buffer_size - offset , " %d " , values [ i ] ) ;
2015-07-15 16:22:57 +02:00
}
2015-07-30 13:16:53 +02:00
return offset ;
2015-07-10 17:16:25 +02:00
}
2015-08-07 21:45:00 +02:00
int join_bitmap ( char * buffer , int buffer_size , uint32_t values , int values_nr ) {
if ( buffer_size < values_nr * 3 ) return 0 ;
2015-08-12 16:52:41 +02:00
2015-08-07 21:45:00 +02:00
int i ;
int offset = 0 ;
for ( i = 0 ; i < values_nr - 1 ; i + + ) {
offset + = snprintf ( buffer + offset , buffer_size - offset , " %d, " , get_bit ( values , i ) ) ; // puts string into buffer
}
2015-08-12 16:52:41 +02:00
2015-08-07 21:45:00 +02:00
if ( i < values_nr ) {
offset + = snprintf ( buffer + offset , buffer_size - offset , " %d " , get_bit ( values , i ) ) ;
}
return offset ;
}
2015-07-17 14:58:57 +02:00
2015-08-05 17:05:16 +02:00
void hfp_emit_event ( hfp_callback_t callback , uint8_t event_subtype , uint8_t value ) {
2015-07-10 17:16:25 +02:00
if ( ! callback ) return ;
uint8_t event [ 4 ] ;
event [ 0 ] = HCI_EVENT_HFP_META ;
event [ 1 ] = sizeof ( event ) - 2 ;
event [ 2 ] = event_subtype ;
event [ 3 ] = value ; // status 0 == OK
( * callback ) ( event , sizeof ( event ) ) ;
}
2015-11-20 17:29:41 +01:00
void hfp_emit_string_event ( hfp_callback_t callback , uint8_t event_subtype , const char * value ) {
if ( ! callback ) return ;
2015-11-25 14:59:08 +01:00
uint8_t event [ 40 ] ;
2015-11-20 17:29:41 +01:00
event [ 0 ] = HCI_EVENT_HFP_META ;
event [ 1 ] = sizeof ( event ) - 2 ;
event [ 2 ] = event_subtype ;
2015-11-20 23:25:54 +01:00
int size = ( strlen ( value ) < sizeof ( event ) - 4 ) ? strlen ( value ) : sizeof ( event ) - 4 ;
2015-11-20 17:29:41 +01:00
strncpy ( ( char * ) & event [ 3 ] , value , size ) ;
2015-11-21 21:11:33 +01:00
event [ 3 + size ] = 0 ;
2015-11-20 17:29:41 +01:00
( * callback ) ( event , sizeof ( event ) ) ;
}
2015-11-12 23:31:31 +01:00
static void hfp_emit_audio_connection_established_event ( hfp_callback_t callback , uint8_t value , uint16_t sco_handle ) {
2015-11-12 17:25:31 +01:00
if ( ! callback ) return ;
uint8_t event [ 6 ] ;
event [ 0 ] = HCI_EVENT_HFP_META ;
event [ 1 ] = sizeof ( event ) - 2 ;
event [ 2 ] = HFP_SUBEVENT_AUDIO_CONNECTION_ESTABLISHED ;
event [ 3 ] = value ; // status 0 == OK
2016-01-31 00:07:32 +01:00
little_endian_store_16 ( event , 4 , sco_handle ) ;
2015-11-12 17:25:31 +01:00
( * callback ) ( event , sizeof ( event ) ) ;
}
2015-07-10 17:16:25 +02:00
2016-01-20 15:38:37 +01:00
btstack_linked_list_t * hfp_get_connections ( ) {
return ( btstack_linked_list_t * ) & hfp_connections ;
2015-07-09 17:03:16 +02:00
}
2015-07-09 15:28:06 +02:00
2015-07-10 17:16:25 +02:00
hfp_connection_t * get_hfp_connection_context_for_rfcomm_cid ( uint16_t cid ) {
2016-01-20 14:50:38 +01:00
btstack_linked_list_iterator_t it ;
btstack_linked_list_iterator_init ( & it , hfp_get_connections ( ) ) ;
while ( btstack_linked_list_iterator_has_next ( & it ) ) {
hfp_connection_t * connection = ( hfp_connection_t * ) btstack_linked_list_iterator_next ( & it ) ;
2015-07-10 17:16:25 +02:00
if ( connection - > rfcomm_cid = = cid ) {
return connection ;
}
}
return NULL ;
}
2015-08-07 21:45:00 +02:00
hfp_connection_t * get_hfp_connection_context_for_bd_addr ( bd_addr_t bd_addr ) {
2016-01-20 14:50:38 +01:00
btstack_linked_list_iterator_t it ;
btstack_linked_list_iterator_init ( & it , hfp_get_connections ( ) ) ;
while ( btstack_linked_list_iterator_has_next ( & it ) ) {
hfp_connection_t * connection = ( hfp_connection_t * ) btstack_linked_list_iterator_next ( & it ) ;
2015-07-09 15:28:06 +02:00
if ( memcmp ( connection - > remote_addr , bd_addr , 6 ) = = 0 ) {
return connection ;
}
}
return NULL ;
}
2015-11-11 15:45:35 +01:00
hfp_connection_t * get_hfp_connection_context_for_sco_handle ( uint16_t handle ) {
2016-01-20 14:50:38 +01:00
btstack_linked_list_iterator_t it ;
btstack_linked_list_iterator_init ( & it , hfp_get_connections ( ) ) ;
while ( btstack_linked_list_iterator_has_next ( & it ) ) {
hfp_connection_t * connection = ( hfp_connection_t * ) btstack_linked_list_iterator_next ( & it ) ;
2015-11-11 15:45:35 +01:00
if ( connection - > sco_handle = = handle ) {
2015-08-05 17:23:23 +02:00
return connection ;
}
}
return NULL ;
}
2015-08-21 17:29:10 +02:00
void hfp_reset_context_flags ( hfp_connection_t * context ) {
2015-08-27 00:08:33 +02:00
if ( ! context ) return ;
2015-11-18 16:34:26 +01:00
context - > ok_pending = 0 ;
2015-08-21 17:29:10 +02:00
context - > send_error = 0 ;
2015-11-26 22:05:31 +01:00
context - > keep_byte = 0 ;
2015-08-21 17:29:10 +02:00
context - > change_status_update_for_individual_ag_indicators = 0 ;
context - > operator_name_changed = 0 ;
context - > enable_extended_audio_gateway_error_report = 0 ;
context - > extended_audio_gateway_error = 0 ;
// establish codecs connection
2015-08-28 00:32:46 +02:00
context - > suggested_codec = 0 ;
2015-10-15 13:55:28 +02:00
context - > negotiated_codec = 0 ;
context - > codec_confirmed = 0 ;
2015-08-21 17:29:10 +02:00
context - > establish_audio_connection = 0 ;
}
2015-07-09 15:28:06 +02:00
static hfp_connection_t * create_hfp_connection_context ( ) {
hfp_connection_t * context = btstack_memory_hfp_connection_get ( ) ;
if ( ! context ) return NULL ;
// init state
2015-08-05 17:05:16 +02:00
memset ( context , 0 , sizeof ( hfp_connection_t ) ) ;
2015-07-09 15:28:06 +02:00
context - > state = HFP_IDLE ;
2015-11-13 17:13:48 +01:00
context - > call_state = HFP_CALL_IDLE ;
context - > codecs_state = HFP_CODECS_IDLE ;
2015-07-30 11:10:50 +02:00
context - > parser_state = HFP_PARSER_CMD_HEADER ;
2015-07-29 15:35:28 +02:00
context - > command = HFP_CMD_NONE ;
2015-08-27 00:08:33 +02:00
context - > negotiated_codec = 0 ;
2015-07-30 17:03:11 +02:00
2015-08-07 21:45:00 +02:00
context - > enable_status_update_for_ag_indicators = 0xFF ;
2015-07-15 16:22:57 +02:00
2015-08-12 14:14:55 +02:00
context - > generic_status_indicators_nr = hfp_generic_status_indicators_nr ;
2015-08-13 00:42:42 +02:00
memcpy ( context - > generic_status_indicators , hfp_generic_status_indicators , hfp_generic_status_indicators_nr * sizeof ( hfp_generic_status_indicator_t ) ) ;
2015-08-12 12:22:53 +02:00
2016-01-20 14:50:38 +01:00
btstack_linked_list_add ( & hfp_connections , ( btstack_linked_item_t * ) context ) ;
2015-07-09 15:28:06 +02:00
return context ;
}
2015-08-05 17:05:16 +02:00
static void remove_hfp_connection_context ( hfp_connection_t * context ) {
2016-01-20 14:50:38 +01:00
btstack_linked_list_remove ( & hfp_connections , ( btstack_linked_item_t * ) context ) ;
2015-08-05 17:05:16 +02:00
}
2015-07-09 15:28:06 +02:00
2015-11-06 21:34:45 +01:00
static hfp_connection_t * provide_hfp_connection_context_for_bd_addr ( bd_addr_t bd_addr ) {
2015-07-09 15:28:06 +02:00
hfp_connection_t * context = get_hfp_connection_context_for_bd_addr ( bd_addr ) ;
if ( context ) return context ;
context = create_hfp_connection_context ( ) ;
2015-11-12 14:16:20 +01:00
printf ( " created context for address %s \n " , bd_addr_to_str ( bd_addr ) ) ;
2015-07-09 15:28:06 +02:00
memcpy ( context - > remote_addr , bd_addr , 6 ) ;
return context ;
}
2015-11-18 16:34:09 +01:00
/* @param network.
* 0 = = no ability to reject a call .
* 1 = = ability to reject a call .
*/
2015-07-09 15:28:06 +02:00
2015-07-10 17:16:25 +02:00
/* @param suported_features
* HF bit 0 : EC and / or NR function ( yes / no , 1 = yes , 0 = no )
* HF bit 1 : Call waiting or three - way calling ( yes / no , 1 = yes , 0 = no )
* HF bit 2 : CLI presentation capability ( yes / no , 1 = yes , 0 = no )
* HF bit 3 : Voice recognition activation ( yes / no , 1 = yes , 0 = no )
* HF bit 4 : Remote volume control ( yes / no , 1 = yes , 0 = no )
* HF bit 5 : Wide band speech ( yes / no , 1 = yes , 0 = no )
*/
/* Bit position:
* AG bit 0 : Three - way calling ( yes / no , 1 = yes , 0 = no )
* AG bit 1 : EC and / or NR function ( yes / no , 1 = yes , 0 = no )
* AG bit 2 : Voice recognition function ( yes / no , 1 = yes , 0 = no )
* AG bit 3 : In - band ring tone capability ( yes / no , 1 = yes , 0 = no )
* AG bit 4 : Attach a phone number to a voice tag ( yes / no , 1 = yes , 0 = no )
* AG bit 5 : Wide band speech ( yes / no , 1 = yes , 0 = no )
*/
2015-11-25 16:09:30 +01:00
void hfp_create_sdp_record ( uint8_t * service , uint32_t service_record_handle , uint16_t service_uuid , int rfcomm_channel_nr , const char * name ) {
2015-07-09 15:28:06 +02:00
uint8_t * attribute ;
de_create_sequence ( service ) ;
// 0x0000 "Service Record Handle"
de_add_number ( service , DE_UINT , DE_SIZE_16 , SDP_ServiceRecordHandle ) ;
2015-11-25 16:09:30 +01:00
de_add_number ( service , DE_UINT , DE_SIZE_32 , service_record_handle ) ;
2015-07-09 15:28:06 +02:00
// 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 ) ;
}
static hfp_connection_t * connection_doing_sdp_query = NULL ;
2016-01-29 21:31:07 +01:00
static void handle_query_rfcomm_event ( uint8_t packet_type , uint8_t * packet , uint16_t size , void * context ) {
2015-07-09 15:28:06 +02:00
hfp_connection_t * connection = connection_doing_sdp_query ;
2015-07-09 17:03:16 +02:00
2016-01-30 23:58:36 +01:00
if ( connection - > state ! = HFP_W4_SDP_EVENT_QUERY_COMPLETE ) return ;
2015-07-09 17:03:16 +02:00
2016-01-29 21:31:07 +01:00
switch ( packet [ 0 ] ) {
2016-01-30 23:58:36 +01:00
case SDP_EVENT_QUERY_RFCOMM_SERVICE :
2015-07-09 15:28:06 +02:00
if ( ! connection ) {
2016-01-29 21:31:07 +01:00
log_error ( " handle_query_rfcomm_event alloc connection for RFCOMM port %u failed " , sdp_query_rfcomm_service_event_get_rfcomm_channel ( packet ) ) ;
2015-07-09 15:28:06 +02:00
return ;
}
2016-01-29 21:31:07 +01:00
connection - > rfcomm_channel_nr = sdp_query_rfcomm_service_event_get_rfcomm_channel ( packet ) ;
2015-07-09 15:28:06 +02:00
break ;
2016-01-30 23:58:36 +01:00
case SDP_EVENT_QUERY_COMPLETE :
2015-07-09 15:28:06 +02:00
connection_doing_sdp_query = NULL ;
if ( connection - > rfcomm_channel_nr > 0 ) {
connection - > state = HFP_W4_RFCOMM_CONNECTED ;
2016-01-30 23:58:36 +01:00
log_info ( " HFP: SDP_EVENT_QUERY_COMPLETE context %p, addr %s, state %d " , connection , bd_addr_to_str ( connection - > remote_addr ) , connection - > state ) ;
2015-11-13 21:08:36 +01:00
rfcomm_create_channel ( connection - > remote_addr , connection - > rfcomm_channel_nr , NULL ) ;
2015-07-09 15:28:06 +02:00
break ;
}
2016-01-29 21:31:07 +01:00
log_info ( " rfcomm service not found, status %u. " , sdp_query_complete_event_get_status ( packet ) ) ;
2015-07-09 15:28:06 +02:00
break ;
default :
break ;
}
}
2015-08-05 17:23:23 +02:00
void hfp_handle_hci_event ( hfp_callback_t callback , uint8_t packet_type , uint8_t * packet , uint16_t size ) {
2015-07-09 17:03:16 +02:00
bd_addr_t event_addr ;
2015-08-05 17:23:23 +02:00
uint16_t rfcomm_cid , handle ;
2015-07-09 17:03:16 +02:00
hfp_connection_t * context = NULL ;
2015-11-12 14:16:20 +01:00
// printf("AG packet_handler type %u, packet[0] %x, size %u\n", packet_type, packet[0], size);
2015-07-10 10:46:45 +02:00
switch ( packet [ 0 ] ) {
2015-07-09 17:03:16 +02:00
case BTSTACK_EVENT_STATE :
// bt stack activated, get started
if ( packet [ 2 ] = = HCI_STATE_WORKING ) {
printf ( " BTstack activated, get started . \n " ) ;
}
break ;
2015-07-09 15:28:06 +02:00
2015-07-09 17:03:16 +02:00
case HCI_EVENT_PIN_CODE_REQUEST :
// inform about pin code request
printf ( " Pin code request - using '0000' \n \r " ) ;
bt_flip_addr ( event_addr , & packet [ 2 ] ) ;
hci_send_cmd ( & hci_pin_code_request_reply , & event_addr , 4 , " 0000 " ) ;
break ;
case RFCOMM_EVENT_INCOMING_CONNECTION :
// data: event (8), len(8), address(48), channel (8), rfcomm_cid (16)
bt_flip_addr ( event_addr , & packet [ 2 ] ) ;
2015-11-18 17:08:31 +01:00
context = provide_hfp_connection_context_for_bd_addr ( event_addr ) ;
2015-07-09 17:03:16 +02:00
2015-08-05 17:05:16 +02:00
if ( ! context | | context - > state ! = HFP_IDLE ) return ;
2015-07-09 17:03:16 +02:00
2016-01-31 00:07:32 +01:00
context - > rfcomm_cid = little_endian_read_16 ( packet , 9 ) ;
2015-07-09 17:03:16 +02:00
context - > state = HFP_W4_RFCOMM_CONNECTED ;
printf ( " RFCOMM channel %u requested for %s \n " , context - > rfcomm_cid , bd_addr_to_str ( context - > remote_addr ) ) ;
2016-01-21 12:09:19 +01:00
rfcomm_accept_connection ( context - > rfcomm_cid ) ;
2015-07-09 17:03:16 +02:00
break ;
case RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE :
// data: event(8), len(8), status (8), address (48), handle(16), server channel(8), rfcomm_cid(16), max frame size(16)
2015-11-12 12:13:43 +01:00
printf ( " RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE packet_handler type %u, packet[0] %x, size %u \n " , packet_type , packet [ 0 ] , size ) ;
2015-11-11 15:45:35 +01:00
2015-07-16 10:26:53 +02:00
bt_flip_addr ( event_addr , & packet [ 3 ] ) ;
2015-08-05 17:05:16 +02:00
context = get_hfp_connection_context_for_bd_addr ( event_addr ) ;
if ( ! context | | context - > state ! = HFP_W4_RFCOMM_CONNECTED ) return ;
2015-07-16 10:26:53 +02:00
2015-07-09 17:03:16 +02:00
if ( packet [ 2 ] ) {
2015-08-05 17:23:23 +02:00
hfp_emit_event ( callback , HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_ESTABLISHED , packet [ 2 ] ) ;
2015-08-05 17:05:16 +02:00
remove_hfp_connection_context ( context ) ;
2015-07-09 17:03:16 +02:00
} else {
2016-01-31 00:07:32 +01:00
context - > con_handle = little_endian_read_16 ( packet , 9 ) ;
2015-11-11 15:45:35 +01:00
printf ( " RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE con_handle 0x%02x \n " , context - > con_handle ) ;
2016-01-31 00:07:32 +01:00
context - > rfcomm_cid = little_endian_read_16 ( packet , 12 ) ;
uint16_t mtu = little_endian_read_16 ( packet , 14 ) ;
2015-07-16 12:47:38 +02:00
printf ( " RFCOMM channel open succeeded. Context %p, RFCOMM Channel ID 0x%02x, max frame size %u \n " , context , context - > rfcomm_cid , mtu ) ;
2015-08-05 17:05:16 +02:00
switch ( context - > state ) {
case HFP_W4_RFCOMM_CONNECTED :
context - > state = HFP_EXCHANGE_SUPPORTED_FEATURES ;
break ;
case HFP_W4_CONNECTION_ESTABLISHED_TO_SHUTDOWN :
context - > state = HFP_W2_DISCONNECT_RFCOMM ;
printf ( " Shutting down RFCOMM. \n " ) ;
break ;
default :
break ;
}
2015-11-18 17:08:31 +01:00
// forward event to app, to learn about con_handle
( * callback ) ( packet , size ) ;
2015-07-09 17:03:16 +02:00
}
break ;
2015-08-05 17:05:16 +02:00
2015-08-21 17:29:10 +02:00
case HCI_EVENT_SYNCHRONOUS_CONNECTION_COMPLETE : {
2015-11-27 22:32:01 +01:00
2015-11-12 12:13:43 +01:00
bt_flip_addr ( event_addr , & packet [ 5 ] ) ;
2015-08-21 17:29:10 +02:00
int index = 2 ;
uint8_t status = packet [ index + + ] ;
2015-11-12 12:13:43 +01:00
if ( status ! = 0 ) {
2015-11-27 22:32:01 +01:00
log_error ( " (e)SCO Connection failed status %u " , status ) ;
// if outgoing && link_setting != d0 && appropriate error
2015-11-27 23:24:20 +01:00
if ( status ! = 0x11 & & status ! = 0x1f ) break ; // invalid params / unspecified error
2015-11-27 22:32:01 +01:00
context = get_hfp_connection_context_for_bd_addr ( event_addr ) ;
if ( ! context ) break ;
switch ( context - > link_setting ) {
case HFP_LINK_SETTINGS_D0 :
return ; // no other option left
case HFP_LINK_SETTINGS_D1 :
// context->link_setting = HFP_LINK_SETTINGS_D0;
// break;
case HFP_LINK_SETTINGS_S1 :
// context->link_setting = HFP_LINK_SETTINGS_D1;
// break;
case HFP_LINK_SETTINGS_S2 :
case HFP_LINK_SETTINGS_S3 :
case HFP_LINK_SETTINGS_S4 :
// context->link_setting = HFP_LINK_SETTINGS_S1;
// break;
case HFP_LINK_SETTINGS_T1 :
case HFP_LINK_SETTINGS_T2 :
// context->link_setting = HFP_LINK_SETTINGS_S3;
context - > link_setting = HFP_LINK_SETTINGS_D0 ;
break ;
}
context - > establish_audio_connection = 1 ;
2015-11-12 12:13:43 +01:00
break ;
}
2016-01-31 00:07:32 +01:00
uint16_t sco_handle = little_endian_read_16 ( packet , index ) ;
2015-08-21 17:29:10 +02:00
index + = 2 ;
2015-11-12 12:13:43 +01:00
bt_flip_addr ( event_addr , & packet [ index ] ) ;
2015-08-21 17:29:10 +02:00
index + = 6 ;
2015-11-12 12:13:43 +01:00
2015-08-21 17:29:10 +02:00
uint8_t link_type = packet [ index + + ] ;
uint8_t transmission_interval = packet [ index + + ] ; // measured in slots
uint8_t retransmission_interval = packet [ index + + ] ; // measured in slots
2016-01-31 00:07:32 +01:00
uint16_t rx_packet_length = little_endian_read_16 ( packet , index ) ; // measured in bytes
2015-08-21 17:29:10 +02:00
index + = 2 ;
2016-01-31 00:07:32 +01:00
uint16_t tx_packet_length = little_endian_read_16 ( packet , index ) ; // measured in bytes
2015-08-21 17:29:10 +02:00
index + = 2 ;
uint8_t air_mode = packet [ index ] ;
switch ( link_type ) {
case 0x00 :
2015-11-12 14:16:20 +01:00
log_info ( " SCO Connection established. " ) ;
2015-08-21 17:29:10 +02:00
if ( transmission_interval ! = 0 ) log_error ( " SCO Connection: transmission_interval not zero: %d. " , transmission_interval ) ;
if ( retransmission_interval ! = 0 ) log_error ( " SCO Connection: retransmission_interval not zero: %d. " , retransmission_interval ) ;
if ( rx_packet_length ! = 0 ) log_error ( " SCO Connection: rx_packet_length not zero: %d. " , rx_packet_length ) ;
if ( tx_packet_length ! = 0 ) log_error ( " SCO Connection: tx_packet_length not zero: %d. " , tx_packet_length ) ;
break ;
case 0x02 :
2015-11-12 12:13:43 +01:00
log_info ( " eSCO Connection established. \n " ) ;
2015-08-21 17:29:10 +02:00
break ;
default :
log_error ( " (e)SCO reserved link_type 0x%2x " , link_type ) ;
break ;
}
log_info ( " sco_handle 0x%2x, address %s, transmission_interval %u slots, retransmission_interval %u slots, "
2015-11-12 14:16:20 +01:00
" rx_packet_length %u bytes, tx_packet_length %u bytes, air_mode 0x%2x (0x02 == CVSD) \n " , sco_handle ,
2015-11-12 12:13:43 +01:00
bd_addr_to_str ( event_addr ) , transmission_interval , retransmission_interval , rx_packet_length , tx_packet_length , air_mode ) ;
2015-08-21 17:29:10 +02:00
2015-11-12 12:13:43 +01:00
context = get_hfp_connection_context_for_bd_addr ( event_addr ) ;
if ( ! context ) {
2015-11-12 14:16:20 +01:00
log_error ( " SCO link created, context for address %s not found. " , bd_addr_to_str ( event_addr ) ) ;
2015-11-12 12:13:43 +01:00
break ;
}
2015-08-21 17:29:10 +02:00
if ( context - > state = = HFP_W4_CONNECTION_ESTABLISHED_TO_SHUTDOWN ) {
2015-11-12 14:16:20 +01:00
log_info ( " SCO about to disconnect: HFP_W4_CONNECTION_ESTABLISHED_TO_SHUTDOWN " ) ;
2015-08-21 17:29:10 +02:00
context - > state = HFP_W2_DISCONNECT_SCO ;
break ;
}
context - > sco_handle = sco_handle ;
2015-11-12 17:25:31 +01:00
context - > establish_audio_connection = 0 ;
2015-08-21 17:29:10 +02:00
context - > state = HFP_AUDIO_CONNECTION_ESTABLISHED ;
2015-11-12 17:25:31 +01:00
hfp_emit_audio_connection_established_event ( callback , packet [ 2 ] , sco_handle ) ;
2015-08-21 17:29:10 +02:00
break ;
}
2015-07-09 17:03:16 +02:00
case RFCOMM_EVENT_CHANNEL_CLOSED :
2016-01-31 00:07:32 +01:00
rfcomm_cid = little_endian_read_16 ( packet , 2 ) ;
2015-08-05 17:05:16 +02:00
context = get_hfp_connection_context_for_rfcomm_cid ( rfcomm_cid ) ;
if ( ! context ) break ;
2015-08-12 23:14:23 +02:00
if ( context - > state = = HFP_W4_RFCOMM_DISCONNECTED_AND_RESTART ) {
context - > state = HFP_IDLE ;
hfp_establish_service_level_connection ( context - > remote_addr , context - > service_uuid ) ;
break ;
}
2015-08-05 17:23:23 +02:00
hfp_emit_event ( callback , HFP_SUBEVENT_SERVICE_LEVEL_CONNECTION_RELEASED , 0 ) ;
2015-08-27 00:08:33 +02:00
remove_hfp_connection_context ( context ) ;
2015-08-05 17:23:23 +02:00
break ;
case HCI_EVENT_DISCONNECTION_COMPLETE :
2016-01-31 00:07:32 +01:00
handle = little_endian_read_16 ( packet , 3 ) ;
2015-11-11 15:45:35 +01:00
context = get_hfp_connection_context_for_sco_handle ( handle ) ;
2015-08-05 17:23:23 +02:00
if ( ! context ) break ;
2015-11-11 15:45:35 +01:00
if ( context - > state ! = HFP_W4_SCO_DISCONNECTED ) {
log_info ( " Received gap disconnect in wrong hfp state " ) ;
}
2015-11-12 12:13:43 +01:00
log_info ( " Check SCO handle: incoming 0x%02x, context 0x%02x \n " , handle , context - > sco_handle ) ;
2015-11-11 15:45:35 +01:00
if ( handle = = context - > sco_handle ) {
2015-11-12 12:13:43 +01:00
log_info ( " SCO disconnected, w2 disconnect RFCOMM \n " ) ;
2015-11-11 15:45:35 +01:00
context - > sco_handle = 0 ;
2015-11-12 12:13:43 +01:00
context - > release_audio_connection = 0 ;
2015-11-19 12:19:43 +01:00
context - > state = HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED ;
hfp_emit_event ( callback , HFP_SUBEVENT_AUDIO_CONNECTION_RELEASED , 0 ) ;
2015-08-12 23:14:23 +02:00
break ;
}
2015-07-09 17:03:16 +02:00
break ;
2015-08-05 17:05:16 +02:00
2015-11-26 10:40:45 +01:00
case HCI_EVENT_INQUIRY_RESULT :
case HCI_EVENT_INQUIRY_RESULT_WITH_RSSI :
case HCI_EVENT_INQUIRY_COMPLETE :
case BTSTACK_EVENT_REMOTE_NAME_CACHED :
case HCI_EVENT_REMOTE_NAME_REQUEST_COMPLETE :
// forward inquiry events to app - TODO: replace with new event handler architecture
( * callback ) ( packet , size ) ;
2015-07-09 17:03:16 +02:00
break ;
}
}
2015-07-09 15:28:06 +02:00
2015-11-18 22:11:27 +01:00
// translates command string into hfp_command_t CMD
2015-11-18 21:39:08 +01:00
static hfp_command_t parse_command ( const char * line_buffer , int isHandsFree ) {
2015-11-18 17:27:45 +01:00
int offset = isHandsFree ? 0 : 2 ;
2015-08-19 14:41:00 +02:00
2015-11-25 17:11:04 +01:00
if ( strncmp ( line_buffer + offset , HFP_LIST_CURRENT_CALLS , strlen ( HFP_LIST_CURRENT_CALLS ) ) = = 0 ) {
return HFP_CMD_LIST_CURRENT_CALLS ;
}
if ( strncmp ( line_buffer + offset , HFP_SUBSCRIBER_NUMBER_INFORMATION , strlen ( HFP_SUBSCRIBER_NUMBER_INFORMATION ) ) = = 0 ) {
return HFP_CMD_GET_SUBSCRIBER_NUMBER_INFORMATION ;
}
2015-11-25 10:50:12 +01:00
if ( strncmp ( line_buffer + offset , HFP_PHONE_NUMBER_FOR_VOICE_TAG , strlen ( HFP_PHONE_NUMBER_FOR_VOICE_TAG ) ) = = 0 ) {
2015-11-28 14:14:55 +01:00
if ( isHandsFree ) return HFP_CMD_AG_SENT_PHONE_NUMBER ;
2015-11-23 16:52:55 +01:00
return HFP_CMD_HF_REQUEST_PHONE_NUMBER ;
}
2015-11-23 21:48:19 +01:00
if ( strncmp ( line_buffer + offset , HFP_TRANSMIT_DTMF_CODES , strlen ( HFP_TRANSMIT_DTMF_CODES ) ) = = 0 ) {
2015-11-23 16:52:55 +01:00
return HFP_CMD_TRANSMIT_DTMF_CODES ;
}
2015-11-23 21:48:19 +01:00
if ( strncmp ( line_buffer + offset , HFP_SET_MICROPHONE_GAIN , strlen ( HFP_SET_MICROPHONE_GAIN ) ) = = 0 ) {
2015-11-23 16:52:55 +01:00
return HFP_CMD_SET_MICROPHONE_GAIN ;
}
2015-11-23 21:48:19 +01:00
if ( strncmp ( line_buffer + offset , HFP_SET_SPEAKER_GAIN , strlen ( HFP_SET_SPEAKER_GAIN ) ) = = 0 ) {
2015-11-23 16:52:55 +01:00
return HFP_CMD_SET_SPEAKER_GAIN ;
2015-08-19 14:41:00 +02:00
}
2015-11-23 21:48:19 +01:00
if ( strncmp ( line_buffer + offset , HFP_ACTIVATE_VOICE_RECOGNITION , strlen ( HFP_ACTIVATE_VOICE_RECOGNITION ) ) = = 0 ) {
2015-11-23 16:22:27 +01:00
if ( isHandsFree ) return HFP_CMD_AG_ACTIVATE_VOICE_RECOGNITION ;
return HFP_CMD_HF_ACTIVATE_VOICE_RECOGNITION ;
2015-08-19 14:41:00 +02:00
}
2015-11-23 21:48:19 +01:00
if ( strncmp ( line_buffer + offset , HFP_TURN_OFF_EC_AND_NR , strlen ( HFP_TURN_OFF_EC_AND_NR ) ) = = 0 ) {
2015-11-23 15:37:09 +01:00
return HFP_CMD_TURN_OFF_EC_AND_NR ;
2015-08-19 14:41:00 +02:00
}
2015-11-19 09:49:38 +01:00
if ( strncmp ( line_buffer , HFP_CALL_ANSWERED , strlen ( HFP_CALL_ANSWERED ) ) = = 0 ) {
2015-11-18 21:39:08 +01:00
return HFP_CMD_CALL_ANSWERED ;
2015-08-19 14:41:00 +02:00
}
2015-11-20 23:25:54 +01:00
if ( strncmp ( line_buffer , HFP_CALL_PHONE_NUMBER , strlen ( HFP_CALL_PHONE_NUMBER ) ) = = 0 ) {
2015-11-20 17:29:41 +01:00
return HFP_CMD_CALL_PHONE_NUMBER ;
}
2015-11-27 23:09:59 +01:00
if ( strncmp ( line_buffer + offset , HFP_REDIAL_LAST_NUMBER , strlen ( HFP_REDIAL_LAST_NUMBER ) ) = = 0 ) {
2015-11-22 18:48:40 +01:00
return HFP_CMD_REDIAL_LAST_NUMBER ;
}
2015-11-19 15:56:06 +01:00
if ( strncmp ( line_buffer + offset , HFP_CHANGE_IN_BAND_RING_TONE_SETTING , strlen ( HFP_CHANGE_IN_BAND_RING_TONE_SETTING ) ) = = 0 ) {
return HFP_CMD_CHANGE_IN_BAND_RING_TONE_SETTING ;
}
2015-11-19 12:53:36 +01:00
if ( strncmp ( line_buffer + offset , HFP_HANG_UP_CALL , strlen ( HFP_HANG_UP_CALL ) ) = = 0 ) {
return HFP_CMD_HANG_UP_CALL ;
}
2015-11-18 19:39:36 +01:00
if ( strncmp ( line_buffer + offset , HFP_ERROR , strlen ( HFP_ERROR ) ) = = 0 ) {
2015-11-18 21:39:08 +01:00
return HFP_CMD_ERROR ;
2015-08-19 14:41:00 +02:00
}
2015-11-27 14:29:41 +01:00
if ( strncmp ( line_buffer + offset , HFP_RING , strlen ( HFP_RING ) ) = = 0 ) {
return HFP_CMD_RING ;
}
2015-11-18 19:39:36 +01:00
if ( isHandsFree & & strncmp ( line_buffer + offset , HFP_OK , strlen ( HFP_OK ) ) = = 0 ) {
2015-11-18 21:39:08 +01:00
return HFP_CMD_OK ;
2015-08-19 14:41:00 +02:00
}
2015-11-18 19:39:36 +01:00
if ( strncmp ( line_buffer + offset , HFP_SUPPORTED_FEATURES , strlen ( HFP_SUPPORTED_FEATURES ) ) = = 0 ) {
2015-11-18 21:39:08 +01:00
return HFP_CMD_SUPPORTED_FEATURES ;
2015-08-19 14:41:00 +02:00
}
2015-11-26 16:02:36 +01:00
if ( strncmp ( line_buffer + offset , HFP_TRANSFER_HF_INDICATOR_STATUS , strlen ( HFP_TRANSFER_HF_INDICATOR_STATUS ) ) = = 0 ) {
return HFP_CMD_HF_INDICATOR_STATUS ;
}
if ( strncmp ( line_buffer + offset , HFP_RESPONSE_AND_HOLD , strlen ( HFP_RESPONSE_AND_HOLD ) ) = = 0 ) {
if ( strncmp ( line_buffer + strlen ( HFP_RESPONSE_AND_HOLD ) + offset , " ? " , 1 ) = = 0 ) {
return HFP_CMD_RESPONSE_AND_HOLD_QUERY ;
}
if ( strncmp ( line_buffer + strlen ( HFP_RESPONSE_AND_HOLD ) + offset , " = " , 1 ) = = 0 ) {
return HFP_CMD_RESPONSE_AND_HOLD_COMMAND ;
}
2015-11-28 16:41:27 +01:00
return HFP_CMD_RESPONSE_AND_HOLD_STATUS ;
2015-11-26 16:02:36 +01:00
}
2015-11-18 19:39:36 +01:00
if ( strncmp ( line_buffer + offset , HFP_INDICATOR , strlen ( HFP_INDICATOR ) ) = = 0 ) {
if ( strncmp ( line_buffer + strlen ( HFP_INDICATOR ) + offset , " ? " , 1 ) = = 0 ) {
2015-11-18 21:39:08 +01:00
return HFP_CMD_RETRIEVE_AG_INDICATORS_STATUS ;
2015-11-18 15:47:08 +01:00
}
2015-11-18 19:39:36 +01:00
if ( strncmp ( line_buffer + strlen ( HFP_INDICATOR ) + offset , " =? " , 2 ) = = 0 ) {
2015-11-18 21:39:08 +01:00
return HFP_CMD_RETRIEVE_AG_INDICATORS ;
2015-08-19 14:41:00 +02:00
}
}
2015-11-18 19:39:36 +01:00
if ( strncmp ( line_buffer + offset , HFP_AVAILABLE_CODECS , strlen ( HFP_AVAILABLE_CODECS ) ) = = 0 ) {
2015-11-18 21:39:08 +01:00
return HFP_CMD_AVAILABLE_CODECS ;
2015-08-19 14:41:00 +02:00
}
2015-11-18 19:39:36 +01:00
if ( strncmp ( line_buffer + offset , HFP_ENABLE_STATUS_UPDATE_FOR_AG_INDICATORS , strlen ( HFP_ENABLE_STATUS_UPDATE_FOR_AG_INDICATORS ) ) = = 0 ) {
2015-11-18 21:39:08 +01:00
return HFP_CMD_ENABLE_INDICATOR_STATUS_UPDATE ;
2015-08-19 14:41:00 +02:00
}
2015-11-22 20:31:17 +01:00
if ( strncmp ( line_buffer + offset , HFP_ENABLE_CLIP , strlen ( HFP_ENABLE_CLIP ) ) = = 0 ) {
return HFP_CMD_ENABLE_CLIP ;
}
2015-11-22 21:14:19 +01:00
if ( strncmp ( line_buffer + offset , HFP_ENABLE_CALL_WAITING_NOTIFICATION , strlen ( HFP_ENABLE_CALL_WAITING_NOTIFICATION ) ) = = 0 ) {
return HFP_CMD_ENABLE_CALL_WAITING_NOTIFICATION ;
}
2015-11-18 19:39:36 +01:00
if ( strncmp ( line_buffer + offset , HFP_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES , strlen ( HFP_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES ) ) = = 0 ) {
2015-11-22 22:40:12 +01:00
if ( isHandsFree ) return HFP_CMD_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES ;
if ( strncmp ( line_buffer + strlen ( HFP_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES ) + offset , " =? " , 2 ) = = 0 ) {
return HFP_CMD_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES ;
}
2015-11-28 17:46:37 +01:00
if ( strncmp ( line_buffer + strlen ( HFP_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES ) + offset , " = " , 1 ) = = 0 ) {
2015-11-22 22:40:12 +01:00
return HFP_CMD_CALL_HOLD ;
}
return HFP_CMD_UNKNOWN ;
2015-08-19 14:41:00 +02:00
}
2015-11-18 19:39:36 +01:00
if ( strncmp ( line_buffer + offset , HFP_GENERIC_STATUS_INDICATOR , strlen ( HFP_GENERIC_STATUS_INDICATOR ) ) = = 0 ) {
2015-11-28 17:46:37 +01:00
if ( isHandsFree ) {
return HFP_CMD_SET_GENERIC_STATUS_INDICATOR_STATUS ;
}
2015-11-18 19:39:36 +01:00
if ( strncmp ( line_buffer + strlen ( HFP_GENERIC_STATUS_INDICATOR ) + offset , " =? " , 2 ) = = 0 ) {
2015-11-18 21:39:08 +01:00
return HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS ;
}
if ( strncmp ( line_buffer + strlen ( HFP_GENERIC_STATUS_INDICATOR ) + offset , " = " , 1 ) = = 0 ) {
return HFP_CMD_LIST_GENERIC_STATUS_INDICATORS ;
2015-08-19 14:41:00 +02:00
}
2015-11-18 22:11:27 +01:00
return HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS_STATE ;
2015-08-19 14:41:00 +02:00
}
2015-11-18 19:39:36 +01:00
if ( strncmp ( line_buffer + offset , HFP_UPDATE_ENABLE_STATUS_FOR_INDIVIDUAL_AG_INDICATORS , strlen ( HFP_UPDATE_ENABLE_STATUS_FOR_INDIVIDUAL_AG_INDICATORS ) ) = = 0 ) {
2015-11-18 21:39:08 +01:00
return HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE ;
2015-08-19 14:41:00 +02:00
}
2015-11-18 19:39:36 +01:00
if ( strncmp ( line_buffer + offset , HFP_QUERY_OPERATOR_SELECTION , strlen ( HFP_QUERY_OPERATOR_SELECTION ) ) = = 0 ) {
if ( strncmp ( line_buffer + strlen ( HFP_QUERY_OPERATOR_SELECTION ) + offset , " = " , 1 ) = = 0 ) {
2015-11-18 21:39:08 +01:00
return HFP_CMD_QUERY_OPERATOR_SELECTION_NAME_FORMAT ;
2015-08-19 14:41:00 +02:00
}
2015-11-18 21:39:08 +01:00
return HFP_CMD_QUERY_OPERATOR_SELECTION_NAME ;
2015-08-19 14:41:00 +02:00
}
2015-11-18 19:39:36 +01:00
if ( strncmp ( line_buffer + offset , HFP_TRANSFER_AG_INDICATOR_STATUS , strlen ( HFP_TRANSFER_AG_INDICATOR_STATUS ) ) = = 0 ) {
2015-11-18 21:39:08 +01:00
return HFP_CMD_TRANSFER_AG_INDICATOR_STATUS ;
2015-08-19 14:41:00 +02:00
}
2015-08-20 12:54:47 +02:00
2015-11-18 19:39:36 +01:00
if ( isHandsFree & & strncmp ( line_buffer + offset , HFP_EXTENDED_AUDIO_GATEWAY_ERROR , strlen ( HFP_EXTENDED_AUDIO_GATEWAY_ERROR ) ) = = 0 ) {
2015-11-18 21:39:08 +01:00
return HFP_CMD_EXTENDED_AUDIO_GATEWAY_ERROR ;
2015-08-20 12:54:47 +02:00
}
2015-11-18 19:39:36 +01:00
if ( ! isHandsFree & & strncmp ( line_buffer + offset , HFP_ENABLE_EXTENDED_AUDIO_GATEWAY_ERROR , strlen ( HFP_ENABLE_EXTENDED_AUDIO_GATEWAY_ERROR ) ) = = 0 ) {
2015-11-18 21:39:08 +01:00
return HFP_CMD_ENABLE_EXTENDED_AUDIO_GATEWAY_ERROR ;
2015-08-20 12:54:47 +02:00
}
2015-08-21 17:29:10 +02:00
2015-11-18 19:39:36 +01:00
if ( strncmp ( line_buffer + offset , HFP_TRIGGER_CODEC_CONNECTION_SETUP , strlen ( HFP_TRIGGER_CODEC_CONNECTION_SETUP ) ) = = 0 ) {
2015-11-18 21:39:08 +01:00
return HFP_CMD_TRIGGER_CODEC_CONNECTION_SETUP ;
2015-08-21 17:29:10 +02:00
}
2015-11-18 19:39:36 +01:00
if ( strncmp ( line_buffer + offset , HFP_CONFIRM_COMMON_CODEC , strlen ( HFP_CONFIRM_COMMON_CODEC ) ) = = 0 ) {
if ( isHandsFree ) {
2015-11-18 21:39:08 +01:00
return HFP_CMD_AG_SUGGESTED_CODEC ;
2015-08-28 00:32:46 +02:00
} else {
2015-11-18 21:39:08 +01:00
return HFP_CMD_HF_CONFIRMED_CODEC ;
2015-08-28 00:32:46 +02:00
}
2015-08-21 17:29:10 +02:00
}
2015-11-11 12:15:35 +01:00
2015-11-18 19:39:36 +01:00
if ( strncmp ( line_buffer + offset , " AT+ " , 3 ) = = 0 ) {
2015-11-28 17:46:37 +01:00
log_info ( " process unknown HF command %s \n " , line_buffer ) ;
2015-11-18 21:39:08 +01:00
return HFP_CMD_UNKNOWN ;
2015-11-12 17:25:31 +01:00
}
2015-11-18 21:39:08 +01:00
2015-11-18 19:39:36 +01:00
if ( strncmp ( line_buffer + offset , " + " , 1 ) = = 0 ) {
2015-11-28 17:46:37 +01:00
log_info ( " process unknown AG command %s \n " , line_buffer ) ;
2015-11-18 21:39:08 +01:00
return HFP_CMD_UNKNOWN ;
2015-11-12 17:25:31 +01:00
}
2015-11-18 19:39:36 +01:00
if ( strncmp ( line_buffer + offset , " NOP " , 3 ) = = 0 ) {
2015-11-18 21:39:08 +01:00
return HFP_CMD_NONE ;
2015-11-12 17:25:31 +01:00
}
2015-11-18 21:39:08 +01:00
return HFP_CMD_NONE ;
2015-08-19 14:41:00 +02:00
}
2015-07-30 17:03:11 +02:00
2015-08-14 14:48:02 +02:00
static void hfp_parser_store_byte ( hfp_connection_t * context , uint8_t byte ) {
2015-11-28 14:14:55 +01:00
// printf("hfp_parser_store_byte %c at pos %u\n", (char) byte, context->line_size);
2015-08-14 14:19:31 +02:00
// TODO: add limit
context - > line_buffer [ context - > line_size + + ] = byte ;
context - > line_buffer [ context - > line_size ] = 0 ;
}
2015-08-14 17:17:04 +02:00
static int hfp_parser_is_buffer_empty ( hfp_connection_t * context ) {
2015-08-14 14:48:02 +02:00
return context - > line_size = = 0 ;
}
2015-08-14 14:19:31 +02:00
2015-08-14 17:17:04 +02:00
static int hfp_parser_is_end_of_line ( uint8_t byte ) {
2015-08-14 14:19:31 +02:00
return byte = = ' \n ' | | byte = = ' \r ' ;
}
2015-08-14 17:17:04 +02:00
static int hfp_parser_is_end_of_header ( uint8_t byte ) {
return hfp_parser_is_end_of_line ( byte ) | | byte = = ' : ' | | byte = = ' ? ' ;
2015-08-14 14:19:31 +02:00
}
2015-08-14 14:48:02 +02:00
static int hfp_parser_found_separator ( hfp_connection_t * context , uint8_t byte ) {
2015-11-26 22:05:31 +01:00
if ( context - > keep_byte = = 1 ) return 1 ;
2015-08-14 17:17:04 +02:00
2015-08-19 17:00:49 +02:00
int found_separator = byte = = ' , ' | | byte = = ' \n ' | | byte = = ' \r ' | |
2015-08-14 14:48:02 +02:00
byte = = ' ) ' | | byte = = ' ( ' | | byte = = ' : ' | |
2015-08-19 17:00:49 +02:00
byte = = ' - ' | | byte = = ' " ' | | byte = = ' ? ' | | byte = = ' = ' ;
2015-08-14 17:17:04 +02:00
return found_separator ;
2015-08-14 14:19:31 +02:00
}
static void hfp_parser_next_state ( hfp_connection_t * context , uint8_t byte ) {
context - > line_size = 0 ;
2015-08-14 17:17:04 +02:00
if ( hfp_parser_is_end_of_line ( byte ) ) {
2015-08-14 14:19:31 +02:00
context - > parser_item_index = 0 ;
2015-08-19 17:00:49 +02:00
context - > parser_state = HFP_PARSER_CMD_HEADER ;
2015-08-14 14:19:31 +02:00
return ;
}
switch ( context - > parser_state ) {
2015-08-14 15:09:57 +02:00
case HFP_PARSER_CMD_HEADER :
2015-08-14 16:35:31 +02:00
context - > parser_state = HFP_PARSER_CMD_SEQUENCE ;
2015-11-26 22:05:31 +01:00
if ( context - > keep_byte = = 1 ) {
2015-08-14 16:35:31 +02:00
hfp_parser_store_byte ( context , byte ) ;
2015-11-26 22:05:31 +01:00
context - > keep_byte = 0 ;
2015-08-14 15:17:03 +02:00
}
2015-08-14 14:48:02 +02:00
break ;
2015-08-14 14:19:31 +02:00
case HFP_PARSER_CMD_SEQUENCE :
2015-08-14 15:51:00 +02:00
switch ( context - > command ) {
2015-11-28 14:14:55 +01:00
case HFP_CMD_AG_SENT_PHONE_NUMBER :
2015-08-14 15:51:00 +02:00
case HFP_CMD_TRANSFER_AG_INDICATOR_STATUS :
2015-11-18 16:04:27 +01:00
case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME :
case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME_FORMAT :
2015-11-18 15:47:08 +01:00
case HFP_CMD_RETRIEVE_AG_INDICATORS :
2015-11-18 16:28:10 +01:00
case HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS_STATE :
2015-11-26 16:02:36 +01:00
case HFP_CMD_HF_INDICATOR_STATUS :
2015-11-18 16:28:10 +01:00
context - > parser_state = HFP_PARSER_SECOND_ITEM ;
2015-08-14 15:51:00 +02:00
break ;
default :
break ;
2015-08-14 15:30:40 +02:00
}
2015-08-14 14:19:31 +02:00
break ;
2015-08-14 16:35:31 +02:00
case HFP_PARSER_SECOND_ITEM :
context - > parser_state = HFP_PARSER_THIRD_ITEM ;
break ;
case HFP_PARSER_THIRD_ITEM :
2015-11-18 15:47:08 +01:00
if ( context - > command = = HFP_CMD_RETRIEVE_AG_INDICATORS ) {
2015-08-14 16:35:31 +02:00
context - > parser_state = HFP_PARSER_CMD_SEQUENCE ;
break ;
}
context - > parser_state = HFP_PARSER_CMD_HEADER ;
break ;
2015-08-14 14:19:31 +02:00
}
}
2015-11-18 17:27:45 +01:00
void hfp_parse ( hfp_connection_t * context , uint8_t byte , int isHandsFree ) {
2015-11-20 23:25:54 +01:00
// handle ATD<dial_string>;
if ( strncmp ( ( const char * ) context - > line_buffer , HFP_CALL_PHONE_NUMBER , strlen ( HFP_CALL_PHONE_NUMBER ) ) = = 0 ) {
// check for end-of-line or ';'
if ( byte = = ' ; ' | | hfp_parser_is_end_of_line ( byte ) ) {
context - > line_buffer [ context - > line_size ] = 0 ;
context - > line_size = 0 ;
context - > command = HFP_CMD_CALL_PHONE_NUMBER ;
} else {
context - > line_buffer [ context - > line_size + + ] = byte ;
}
return ;
}
2015-08-14 17:17:04 +02:00
// TODO: handle space inside word
2015-08-20 12:54:47 +02:00
if ( byte = = ' ' & & context - > parser_state > HFP_PARSER_CMD_HEADER ) return ;
2015-11-26 22:05:31 +01:00
if ( byte = = ' , ' & & context - > command = = HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE ) {
if ( context - > line_size = = 0 ) {
context - > line_buffer [ 0 ] = 0 ;
context - > ignore_value = 1 ;
parse_sequence ( context ) ;
return ;
}
}
2015-08-14 17:17:04 +02:00
if ( ! hfp_parser_found_separator ( context , byte ) ) {
hfp_parser_store_byte ( context , byte ) ;
return ;
2015-11-26 11:55:35 +01:00
}
2015-08-19 17:00:49 +02:00
if ( hfp_parser_is_end_of_line ( byte ) ) {
if ( hfp_parser_is_buffer_empty ( context ) ) {
context - > parser_state = HFP_PARSER_CMD_HEADER ;
}
}
2015-08-14 17:17:04 +02:00
if ( hfp_parser_is_buffer_empty ( context ) ) return ;
2015-07-30 17:03:11 +02:00
switch ( context - > parser_state ) {
case HFP_PARSER_CMD_HEADER : // header
2015-08-14 16:35:31 +02:00
if ( byte = = ' = ' ) {
2015-11-26 22:05:31 +01:00
context - > keep_byte = 1 ;
2015-08-14 16:35:31 +02:00
hfp_parser_store_byte ( context , byte ) ;
2015-08-14 17:17:04 +02:00
return ;
2015-08-14 16:35:31 +02:00
}
if ( byte = = ' ? ' ) {
2015-11-26 22:05:31 +01:00
context - > keep_byte = 0 ;
2015-08-14 16:35:31 +02:00
hfp_parser_store_byte ( context , byte ) ;
2015-08-14 17:17:04 +02:00
return ;
2015-08-13 20:55:21 +02:00
}
2015-11-26 22:05:31 +01:00
if ( byte = = ' , ' ) {
context - > resolve_byte = 1 ;
}
// printf(" parse header 2 %s, keep separator $ %d\n", context->line_buffer, context->keep_byte);
if ( hfp_parser_is_end_of_header ( byte ) | | context - > keep_byte = = 1 ) {
// printf(" parse header 3 %s, keep separator $ %d\n", context->line_buffer, context->keep_byte);
2015-11-18 21:39:08 +01:00
char * line_buffer = ( char * ) context - > line_buffer ;
context - > command = parse_command ( line_buffer , isHandsFree ) ;
/* resolve command name according to context */
if ( context - > command = = HFP_CMD_UNKNOWN ) {
switch ( context - > state ) {
case HFP_W4_LIST_GENERIC_STATUS_INDICATORS :
context - > command = HFP_CMD_LIST_GENERIC_STATUS_INDICATORS ;
break ;
case HFP_W4_RETRIEVE_GENERIC_STATUS_INDICATORS :
context - > command = HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS ;
break ;
case HFP_W4_RETRIEVE_INITITAL_STATE_GENERIC_STATUS_INDICATORS :
context - > command = HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS_STATE ;
break ;
case HFP_W4_RETRIEVE_INDICATORS_STATUS :
context - > command = HFP_CMD_RETRIEVE_AG_INDICATORS_STATUS ;
break ;
case HFP_W4_RETRIEVE_INDICATORS :
2015-12-18 15:11:08 +01:00
context - > send_ag_indicators_segment = 0 ;
2015-11-18 21:39:08 +01:00
context - > command = HFP_CMD_RETRIEVE_AG_INDICATORS ;
break ;
default :
break ;
}
}
2015-07-30 17:03:11 +02:00
}
break ;
2015-11-26 11:55:35 +01:00
case HFP_PARSER_CMD_SEQUENCE :
2015-11-26 22:05:31 +01:00
parse_sequence ( context ) ;
2015-07-30 17:03:11 +02:00
break ;
2015-08-14 14:19:31 +02:00
case HFP_PARSER_SECOND_ITEM :
switch ( context - > command ) {
2015-11-18 16:04:27 +01:00
case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME :
2015-11-26 11:55:35 +01:00
log_info ( " format %s, " , context - > line_buffer ) ;
2015-11-18 16:04:27 +01:00
context - > network_operator . format = atoi ( ( char * ) & context - > line_buffer [ 0 ] ) ;
2015-08-14 14:19:31 +02:00
break ;
2015-11-18 16:04:27 +01:00
case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME_FORMAT :
2015-11-26 11:55:35 +01:00
log_info ( " format %s \n " , context - > line_buffer ) ;
2015-11-18 16:04:27 +01:00
context - > network_operator . format = atoi ( ( char * ) & context - > line_buffer [ 0 ] ) ;
2015-08-14 14:19:31 +02:00
break ;
2015-11-18 16:28:10 +01:00
case HFP_CMD_LIST_GENERIC_STATUS_INDICATORS :
case HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS :
case HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS_STATE :
2015-08-14 14:53:43 +02:00
context - > generic_status_indicators [ context - > parser_item_index ] . state = ( uint8_t ) atoi ( ( char * ) context - > line_buffer ) ;
break ;
case HFP_CMD_TRANSFER_AG_INDICATOR_STATUS :
context - > ag_indicators [ context - > parser_item_index ] . status = ( uint8_t ) atoi ( ( char * ) context - > line_buffer ) ;
2015-11-26 11:55:35 +01:00
log_info ( " %d \n " , context - > ag_indicators [ context - > parser_item_index ] . status ) ;
2015-08-14 14:53:43 +02:00
context - > ag_indicators [ context - > parser_item_index ] . status_changed = 1 ;
break ;
2015-11-18 15:47:08 +01:00
case HFP_CMD_RETRIEVE_AG_INDICATORS :
context - > ag_indicators [ context - > parser_item_index ] . min_range = atoi ( ( char * ) context - > line_buffer ) ;
log_info ( " %s, " , context - > line_buffer ) ;
2015-08-14 15:12:31 +02:00
break ;
2015-11-28 14:14:55 +01:00
case HFP_CMD_AG_SENT_PHONE_NUMBER :
context - > bnip_type = ( uint8_t ) atoi ( ( char * ) context - > line_buffer ) ;
break ;
2015-08-14 14:19:31 +02:00
default :
break ;
}
break ;
case HFP_PARSER_THIRD_ITEM :
2015-08-14 17:17:04 +02:00
switch ( context - > command ) {
2015-11-18 16:04:27 +01:00
case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME :
strcpy ( context - > network_operator . name , ( char * ) context - > line_buffer ) ;
log_info ( " name %s \n " , context - > line_buffer ) ;
2015-08-14 14:19:31 +02:00
break ;
2015-11-18 15:47:08 +01:00
case HFP_CMD_RETRIEVE_AG_INDICATORS :
context - > ag_indicators [ context - > parser_item_index ] . max_range = atoi ( ( char * ) context - > line_buffer ) ;
context - > parser_item_index + + ;
context - > ag_indicators_nr = context - > parser_item_index ;
log_info ( " %s) \n " , context - > line_buffer ) ;
2015-08-14 15:12:31 +02:00
break ;
2015-08-14 14:19:31 +02:00
default :
break ;
2015-08-13 15:01:19 +02:00
}
break ;
2015-07-30 17:03:11 +02:00
}
2015-08-14 17:17:04 +02:00
hfp_parser_next_state ( context , byte ) ;
2015-11-26 22:05:31 +01:00
if ( context - > resolve_byte & & context - > command = = HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE ) {
context - > resolve_byte = 0 ;
context - > ignore_value = 1 ;
parse_sequence ( context ) ;
context - > line_buffer [ 0 ] = 0 ;
context - > line_size = 0 ;
}
}
static void parse_sequence ( hfp_connection_t * context ) {
int value ;
switch ( context - > command ) {
2015-11-28 17:46:37 +01:00
case HFP_CMD_SET_GENERIC_STATUS_INDICATOR_STATUS :
value = atoi ( ( char * ) & context - > line_buffer [ 0 ] ) ;
int i ;
switch ( context - > parser_item_index ) {
case 0 :
for ( i = 0 ; i < context - > generic_status_indicators_nr ; i + + ) {
if ( context - > generic_status_indicators [ i ] . uuid = = value ) {
context - > parser_indicator_index = i ;
break ;
}
}
break ;
case 1 :
if ( context - > parser_indicator_index < 0 ) break ;
context - > generic_status_indicators [ context - > parser_indicator_index ] . state = value ;
log_info ( " HFP_CMD_SET_GENERIC_STATUS_INDICATOR_STATUS set indicator at index %u, to %u \n " ,
context - > parser_item_index , value ) ;
break ;
default :
break ;
}
context - > parser_item_index + + ;
break ;
2015-11-28 16:55:11 +01:00
case HFP_CMD_GET_SUBSCRIBER_NUMBER_INFORMATION :
switch ( context - > parser_item_index ) {
case 0 :
strncpy ( context - > bnip_number , ( char * ) context - > line_buffer , sizeof ( context - > bnip_number ) ) ;
context - > bnip_number [ sizeof ( context - > bnip_number ) - 1 ] = 0 ;
break ;
case 1 :
value = atoi ( ( char * ) & context - > line_buffer [ 0 ] ) ;
context - > bnip_type = value ;
break ;
default :
break ;
}
context - > parser_item_index + + ;
break ;
2015-11-28 16:07:40 +01:00
case HFP_CMD_LIST_CURRENT_CALLS :
switch ( context - > parser_item_index ) {
case 0 :
value = atoi ( ( char * ) & context - > line_buffer [ 0 ] ) ;
context - > clcc_idx = value ;
break ;
case 1 :
value = atoi ( ( char * ) & context - > line_buffer [ 0 ] ) ;
context - > clcc_dir = value ;
break ;
case 2 :
value = atoi ( ( char * ) & context - > line_buffer [ 0 ] ) ;
context - > clcc_status = value ;
break ;
case 3 :
value = atoi ( ( char * ) & context - > line_buffer [ 0 ] ) ;
context - > clcc_mpty = value ;
break ;
case 4 :
strncpy ( context - > bnip_number , ( char * ) context - > line_buffer , sizeof ( context - > bnip_number ) ) ;
context - > bnip_number [ sizeof ( context - > bnip_number ) - 1 ] = 0 ;
break ;
case 5 :
value = atoi ( ( char * ) & context - > line_buffer [ 0 ] ) ;
context - > bnip_type = value ;
break ;
default :
break ;
}
context - > parser_item_index + + ;
break ;
2015-11-26 22:05:31 +01:00
case HFP_CMD_SET_MICROPHONE_GAIN :
value = atoi ( ( char * ) & context - > line_buffer [ 0 ] ) ;
context - > microphone_gain = value ;
log_info ( " hfp parse HFP_CMD_SET_MICROPHONE_GAIN %d \n " , value ) ;
break ;
case HFP_CMD_SET_SPEAKER_GAIN :
value = atoi ( ( char * ) & context - > line_buffer [ 0 ] ) ;
context - > speaker_gain = value ;
log_info ( " hfp parse HFP_CMD_SET_SPEAKER_GAIN %d \n " , value ) ;
break ;
case HFP_CMD_HF_ACTIVATE_VOICE_RECOGNITION :
value = atoi ( ( char * ) & context - > line_buffer [ 0 ] ) ;
context - > ag_activate_voice_recognition = value ;
log_info ( " hfp parse HFP_CMD_HF_ACTIVATE_VOICE_RECOGNITION %d \n " , value ) ;
break ;
case HFP_CMD_TURN_OFF_EC_AND_NR :
value = atoi ( ( char * ) & context - > line_buffer [ 0 ] ) ;
context - > ag_echo_and_noise_reduction = value ;
log_info ( " hfp parse HFP_CMD_TURN_OFF_EC_AND_NR %d \n " , value ) ;
break ;
case HFP_CMD_CHANGE_IN_BAND_RING_TONE_SETTING :
value = atoi ( ( char * ) & context - > line_buffer [ 0 ] ) ;
context - > remote_supported_features = store_bit ( context - > remote_supported_features , HFP_AGSF_IN_BAND_RING_TONE , value ) ;
log_info ( " hfp parse HFP_CHANGE_IN_BAND_RING_TONE_SETTING %d \n " , value ) ;
break ;
case HFP_CMD_HF_CONFIRMED_CODEC :
context - > codec_confirmed = atoi ( ( char * ) context - > line_buffer ) ;
log_info ( " hfp parse HFP_CMD_HF_CONFIRMED_CODEC %d \n " , context - > codec_confirmed ) ;
break ;
case HFP_CMD_AG_SUGGESTED_CODEC :
context - > suggested_codec = atoi ( ( char * ) context - > line_buffer ) ;
log_info ( " hfp parse HFP_CMD_AG_SUGGESTED_CODEC %d \n " , context - > suggested_codec ) ;
break ;
case HFP_CMD_SUPPORTED_FEATURES :
context - > remote_supported_features = atoi ( ( char * ) context - > line_buffer ) ;
log_info ( " Parsed supported feature %d \n " , context - > remote_supported_features ) ;
break ;
case HFP_CMD_AVAILABLE_CODECS :
log_info ( " Parsed codec %s \n " , context - > line_buffer ) ;
context - > remote_codecs [ context - > parser_item_index ] = ( uint16_t ) atoi ( ( char * ) context - > line_buffer ) ;
context - > parser_item_index + + ;
context - > remote_codecs_nr = context - > parser_item_index ;
break ;
case HFP_CMD_RETRIEVE_AG_INDICATORS :
strcpy ( ( char * ) context - > ag_indicators [ context - > parser_item_index ] . name , ( char * ) context - > line_buffer ) ;
context - > ag_indicators [ context - > parser_item_index ] . index = context - > parser_item_index + 1 ;
log_info ( " Indicator %d: %s ( " , context - > ag_indicators_nr + 1 , context - > line_buffer ) ;
break ;
case HFP_CMD_RETRIEVE_AG_INDICATORS_STATUS :
log_info ( " Parsed Indicator %d with status: %s \n " , context - > parser_item_index + 1 , context - > line_buffer ) ;
context - > ag_indicators [ context - > parser_item_index ] . status = atoi ( ( char * ) context - > line_buffer ) ;
context - > parser_item_index + + ;
break ;
case HFP_CMD_ENABLE_INDICATOR_STATUS_UPDATE :
context - > parser_item_index + + ;
if ( context - > parser_item_index ! = 4 ) break ;
log_info ( " Parsed Enable indicators: %s \n " , context - > line_buffer ) ;
value = atoi ( ( char * ) & context - > line_buffer [ 0 ] ) ;
context - > enable_status_update_for_ag_indicators = ( uint8_t ) value ;
break ;
case HFP_CMD_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES :
log_info ( " Parsed Support call hold: %s \n " , context - > line_buffer ) ;
if ( context - > line_size > 2 ) break ;
strcpy ( ( char * ) context - > remote_call_services [ context - > remote_call_services_nr ] . name , ( char * ) context - > line_buffer ) ;
context - > remote_call_services_nr + + ;
break ;
case HFP_CMD_LIST_GENERIC_STATUS_INDICATORS :
case HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS :
log_info ( " Parsed Generic status indicator: %s \n " , context - > line_buffer ) ;
context - > generic_status_indicators [ context - > parser_item_index ] . uuid = ( uint16_t ) atoi ( ( char * ) context - > line_buffer ) ;
context - > parser_item_index + + ;
context - > generic_status_indicators_nr = context - > parser_item_index ;
break ;
case HFP_CMD_RETRIEVE_GENERIC_STATUS_INDICATORS_STATE :
// HF parses inital AG gen. ind. state
log_info ( " Parsed List generic status indicator %s state: " , context - > line_buffer ) ;
context - > parser_item_index = ( uint8_t ) atoi ( ( char * ) context - > line_buffer ) ;
break ;
2015-11-26 22:09:48 +01:00
case HFP_CMD_HF_INDICATOR_STATUS :
context - > parser_indicator_index = ( uint8_t ) atoi ( ( char * ) context - > line_buffer ) ;
log_info ( " Parsed HF indicator index %u " , context - > parser_indicator_index ) ;
break ;
2015-11-26 22:05:31 +01:00
case HFP_CMD_ENABLE_INDIVIDUAL_AG_INDICATOR_STATUS_UPDATE :
// AG parses new gen. ind. state
if ( context - > ignore_value ) {
context - > ignore_value = 0 ;
2015-11-26 22:39:35 +01:00
log_info ( " Parsed Enable AG indicator pos %u('%s') - unchanged (stays %u) \n " , context - > parser_item_index ,
context - > ag_indicators [ context - > parser_item_index ] . name , context - > ag_indicators [ context - > parser_item_index ] . enabled ) ;
2015-11-26 22:05:31 +01:00
}
else if ( context - > ag_indicators [ context - > parser_item_index ] . mandatory ) {
log_info ( " Parsed Enable AG indicator pos %u('%s') - ignore (mandatory) \n " ,
context - > parser_item_index , context - > ag_indicators [ context - > parser_item_index ] . name ) ;
} else {
value = atoi ( ( char * ) & context - > line_buffer [ 0 ] ) ;
context - > ag_indicators [ context - > parser_item_index ] . enabled = value ;
log_info ( " Parsed Enable AG indicator pos %u('%s'): %u \n " , context - > parser_item_index ,
context - > ag_indicators [ context - > parser_item_index ] . name , value ) ;
}
context - > parser_item_index + + ;
break ;
case HFP_CMD_TRANSFER_AG_INDICATOR_STATUS :
// indicators are indexed starting with 1
context - > parser_item_index = atoi ( ( char * ) & context - > line_buffer [ 0 ] ) - 1 ;
log_info ( " Parsed status of the AG indicator %d, status " , context - > parser_item_index ) ;
break ;
case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME :
context - > network_operator . mode = atoi ( ( char * ) & context - > line_buffer [ 0 ] ) ;
log_info ( " Parsed network operator mode: %d, " , context - > network_operator . mode ) ;
break ;
case HFP_CMD_QUERY_OPERATOR_SELECTION_NAME_FORMAT :
if ( context - > line_buffer [ 0 ] = = ' 3 ' ) {
log_info ( " Parsed Set network operator format : %s, " , context - > line_buffer ) ;
break ;
}
// TODO emit ERROR, wrong format
log_info ( " ERROR Set network operator format: index %s not supported \n " , context - > line_buffer ) ;
break ;
case HFP_CMD_ERROR :
break ;
case HFP_CMD_EXTENDED_AUDIO_GATEWAY_ERROR :
context - > extended_audio_gateway_error = ( uint8_t ) atoi ( ( char * ) context - > line_buffer ) ;
break ;
case HFP_CMD_ENABLE_EXTENDED_AUDIO_GATEWAY_ERROR :
context - > enable_extended_audio_gateway_error_report = ( uint8_t ) atoi ( ( char * ) context - > line_buffer ) ;
context - > ok_pending = 1 ;
context - > extended_audio_gateway_error = 0 ;
break ;
2015-11-28 14:14:55 +01:00
case HFP_CMD_AG_SENT_PHONE_NUMBER :
strncpy ( context - > bnip_number , ( char * ) context - > line_buffer , sizeof ( context - > bnip_number ) ) ;
context - > bnip_number [ sizeof ( context - > bnip_number ) - 1 ] = 0 ;
break ;
2015-11-26 22:05:31 +01:00
default :
break ;
2015-11-26 22:09:48 +01:00
}
2015-07-30 17:03:11 +02:00
}
2015-07-09 15:28:06 +02:00
void hfp_init ( uint16_t rfcomm_channel_nr ) {
2015-11-13 22:36:27 +01:00
rfcomm_register_service ( rfcomm_channel_nr , 0xffff ) ;
2015-07-09 15:28:06 +02:00
sdp_query_rfcomm_register_callback ( handle_query_rfcomm_event , NULL ) ;
}
2015-08-06 10:22:02 +02:00
void hfp_establish_service_level_connection ( bd_addr_t bd_addr , uint16_t service_uuid ) {
2015-08-05 17:05:16 +02:00
hfp_connection_t * context = provide_hfp_connection_context_for_bd_addr ( bd_addr ) ;
log_info ( " hfp_connect %s, context %p " , bd_addr_to_str ( bd_addr ) , context ) ;
2015-07-16 10:26:53 +02:00
2015-08-05 17:05:16 +02:00
if ( ! context ) {
2015-08-07 21:45:00 +02:00
log_error ( " hfp_establish_service_level_connection for addr %s failed " , bd_addr_to_str ( bd_addr ) ) ;
2015-07-09 15:28:06 +02:00
return ;
}
2015-11-13 17:13:48 +01:00
2015-08-12 23:14:23 +02:00
switch ( context - > state ) {
case HFP_W2_DISCONNECT_RFCOMM :
context - > state = HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED ;
return ;
case HFP_W4_RFCOMM_DISCONNECTED :
context - > state = HFP_W4_RFCOMM_DISCONNECTED_AND_RESTART ;
return ;
case HFP_IDLE :
memcpy ( context - > remote_addr , bd_addr , 6 ) ;
2016-01-30 23:58:36 +01:00
context - > state = HFP_W4_SDP_EVENT_QUERY_COMPLETE ;
2015-08-12 23:14:23 +02:00
connection_doing_sdp_query = context ;
context - > service_uuid = service_uuid ;
sdp_query_rfcomm_channel_and_name_for_uuid ( context - > remote_addr , service_uuid ) ;
break ;
default :
break ;
}
2015-08-05 17:05:16 +02:00
}
2015-07-10 17:16:25 +02:00
2015-08-07 21:45:00 +02:00
void hfp_release_service_level_connection ( hfp_connection_t * context ) {
2015-08-21 17:29:10 +02:00
if ( ! context ) return ;
2015-08-27 00:08:33 +02:00
if ( context - > state < HFP_W4_RFCOMM_CONNECTED ) {
context - > state = HFP_IDLE ;
return ;
2015-08-05 17:05:16 +02:00
}
2015-08-27 00:08:33 +02:00
if ( context - > state = = HFP_W4_RFCOMM_CONNECTED ) {
context - > state = HFP_W4_CONNECTION_ESTABLISHED_TO_SHUTDOWN ;
return ;
}
2015-11-11 15:45:35 +01:00
if ( context - > state < HFP_W4_SCO_CONNECTED ) {
2015-08-27 00:08:33 +02:00
context - > state = HFP_W2_DISCONNECT_RFCOMM ;
return ;
}
if ( context - > state < HFP_W4_SCO_DISCONNECTED ) {
context - > state = HFP_W2_DISCONNECT_SCO ;
return ;
}
2015-08-07 21:45:00 +02:00
return ;
2015-07-09 17:03:16 +02:00
}
2015-11-12 12:13:43 +01:00
void hfp_release_audio_connection ( hfp_connection_t * context ) {
if ( ! context ) return ;
if ( context - > state > = HFP_W2_DISCONNECT_SCO ) return ;
context - > release_audio_connection = 1 ;
2015-08-21 17:29:10 +02:00
}
2015-07-30 13:16:53 +02:00
2015-11-27 22:32:01 +01:00
static const struct link_settings {
const uint16_t max_latency ;
const uint8_t retransmission_effort ;
const uint16_t packet_types ;
} hfp_link_settings [ ] = {
{ 0xffff , 0xff , 0x03c1 } , // HFP_LINK_SETTINGS_D0, HV1
{ 0xffff , 0xff , 0x03c4 } , // HFP_LINK_SETTINGS_D1, HV3
{ 0x0007 , 0x01 , 0x03c8 } , // HFP_LINK_SETTINGS_S1, EV3
{ 0x0007 , 0x01 , 0x0380 } , // HFP_LINK_SETTINGS_S2, 2-EV3
{ 0x000a , 0x01 , 0x0380 } , // HFP_LINK_SETTINGS_S3, 2-EV3
{ 0x000c , 0x02 , 0x0380 } , // HFP_LINK_SETTINGS_S4, 2-EV3
{ 0x0008 , 0x02 , 0x03c8 } , // HFP_LINK_SETTINGS_T1, EV3
{ 0x000d , 0x02 , 0x0380 } // HFP_LINK_SETTINGS_T2, 2-EV3
} ;
2015-08-28 00:32:46 +02:00
2015-11-27 22:32:01 +01:00
void hfp_setup_synchronous_connection ( hci_con_handle_t handle , hfp_link_setttings_t setting ) {
// all packet types, fixed bandwidth
log_info ( " hfp_setup_synchronous_connection using setting nr %u " , setting ) ;
hci_send_cmd ( & hci_setup_synchronous_connection , handle , 8000 , 8000 , hfp_link_settings [ setting ] . max_latency ,
hci_get_sco_voice_setting ( ) , hfp_link_settings [ setting ] . retransmission_effort , hfp_link_settings [ setting ] . packet_types ) ; // all types 0x003f, only 2-ev3 0x380
}