diff --git a/example/embedded/Makefile.inc b/example/embedded/Makefile.inc index 07bc42978..faaa0c28a 100644 --- a/example/embedded/Makefile.inc +++ b/example/embedded/Makefile.inc @@ -167,7 +167,7 @@ hsp_hs_test: ${CORE_OBJ} ${COMMON_OBJ} ${SDP_CLIENT} hsp_hs.o hsp_hs_test.c clean: rm -f ${EXAMPLES} - rm -f *.o *.out *.hex + rm -f *.o *.out *.hex *.exe rm -f ancs_client_demo.h profile.h spp_and_le_counter.h rm -rf *.dSYM rm -rf ${BTSTACK_ROOT}/ble/*.o diff --git a/example/embedded/hsp_hs_test.c b/example/embedded/hsp_hs_test.c index f0edfc3c6..deab93e95 100644 --- a/example/embedded/hsp_hs_test.c +++ b/example/embedded/hsp_hs_test.c @@ -241,7 +241,7 @@ int btstack_main(int argc, const char * argv[]){ sdp_init(); memset(hsp_service_buffer, 0, sizeof(hsp_service_buffer)); - hsp_hs_create_service(hsp_service_buffer, 0x10001, rfcomm_channel_nr, hsp_hs_service_name, 0); + hsp_hs_create_sdp_record(hsp_service_buffer, 0x10001, rfcomm_channel_nr, hsp_hs_service_name, 0); sdp_register_service(hsp_service_buffer); // turn on! diff --git a/port/libusb/hci_transport_h2_libusb.c b/port/libusb/hci_transport_h2_libusb.c index a69157f23..20bb23ffd 100644 --- a/port/libusb/hci_transport_h2_libusb.c +++ b/port/libusb/hci_transport_h2_libusb.c @@ -138,6 +138,10 @@ static struct libusb_transfer *acl_in_transfer[ASYNC_BUFFERS]; #ifdef HAVE_SCO +#ifdef _WIN32 +#error "SCO not working on Win32 (Windows 8, libusb 1.0.19, Zadic WinUSB), please uncomment HAVE_SCO in btstack-config.h for now" +#endif + // incoming SCO static H2_SCO_STATE sco_state; static uint8_t sco_buffer[255+3 + SCO_PACKET_SIZE]; diff --git a/src/classic/hfp_ag.c b/src/classic/hfp_ag.c index 587975a51..dc2ec2e61 100644 --- a/src/classic/hfp_ag.c +++ b/src/classic/hfp_ag.c @@ -37,7 +37,7 @@ // ***************************************************************************** // -// Minimal setup for HFP Audio Gateway (AG) unit (!! UNDER DEVELOPMENT !!) +// HFP Audio Gateway (AG) unit // // ***************************************************************************** diff --git a/src/classic/hfp_ag.h b/src/classic/hfp_ag.h index 862298461..2acb3f9f6 100644 --- a/src/classic/hfp_ag.h +++ b/src/classic/hfp_ag.h @@ -37,7 +37,7 @@ // ***************************************************************************** // -// Minimal setup for HFP Audio Gateway (AG) unit (!! UNDER DEVELOPMENT !!) +// HFP Audio Gateway (AG) unit // // ***************************************************************************** @@ -80,6 +80,14 @@ void hfp_ag_init(uint16_t rfcomm_channel_nr, uint32_t supported_features, */ void hfp_ag_register_packet_handler(hfp_callback_t callback); +/** + * @brief Enable in-band ring tone + */ +void hfp_ag_set_use_in_band_ring_tone(int use_in_band_ring_tone); + + +// actions used by local device / user + /** * @brief Establish RFCOMM connection, and perform service level connection agreement: * - exchange of supported features @@ -93,72 +101,10 @@ void hfp_ag_establish_service_level_connection(bd_addr_t bd_addr); /** * @brief Release the RFCOMM channel and the audio connection between the HF and the AG. - * TODO: trigger release of the audio connection + * TODO: trigger release of the audio connection ?? */ void hfp_ag_release_service_level_connection(bd_addr_t bd_addr); -/** - * @brief Report Extended Audio Gateway Error result codes in the AG. - * Whenever there is an error relating to the functionality of the AG as a - * result of AT command, the AG shall send +CME ERROR: - * - +CME ERROR: 0 - AG failure - * - +CME ERROR: 1 - no connection to phone - * - +CME ERROR: 3 - operation not allowed - * - +CME ERROR: 4 - operation not supported - * - +CME ERROR: 5 - PH-SIM PIN required - * - +CME ERROR: 10 - SIM not inserted - * - +CME ERROR: 11 - SIM PIN required - * - +CME ERROR: 12 - SIM PUK required - * - +CME ERROR: 13 - SIM failure - * - +CME ERROR: 14 - SIM busy - * - +CME ERROR: 16 - incorrect password - * - +CME ERROR: 17 - SIM PIN2 required - * - +CME ERROR: 18 - SIM PUK2 required - * - +CME ERROR: 20 - memory full - * - +CME ERROR: 21 - invalid index - * - +CME ERROR: 23 - memory failure - * - +CME ERROR: 24 - text string too long - * - +CME ERROR: 25 - invalid characters in text string - * - +CME ERROR: 26 - dial string too long - * - +CME ERROR: 27 - invalid characters in dial string - * - +CME ERROR: 30 - no network service - * - +CME ERROR: 31 - network Timeout. - * - +CME ERROR: 32 - network not allowed – Emergency calls only - */ -void hfp_ag_report_extended_audio_gateway_error_result_code(bd_addr_t bd_addr, hfp_cme_error_t error); - -/** - * @brief Report the change in AG's call status. - * Call status: - * - 0 = No calls (held or active) - * - 1 = Call is present (active or held) - */ -void hfp_ag_transfer_call_status(bd_addr_t bd_addr, hfp_call_status_t status); - -/** - * @brief Report the change in AG's call setup status. - * Call setup status: - * - 0 = No call setup in progress - * - 1 = Incoming call setup in progress - * - 2 = Outgoing call setup in dialing state - * - 3 = Outgoing call setup in alerting state - */ -void hfp_ag_transfer_callsetup_status(bd_addr_t bd_addr, hfp_callsetup_status_t status); - -/** - * @brief Report the change in AG's held call status. - * Held call status: - * - 0 = No calls held - * - 1 = Call is placed on hold or active/held calls are swapped - * - 2 = Call on hold, no active calls - */ -void hfp_ag_transfer_callheld_status(bd_addr_t bd_addr, hfp_callheld_status_t status); - -/** - * @brief - */ -void hfp_ag_negotiate_codecs(bd_addr_t bd_addr); - /** * @brief */ @@ -169,11 +115,76 @@ void hfp_ag_establish_audio_connection(bd_addr_t bd_addr); */ void hfp_ag_release_audio_connection(bd_addr_t bd_addr); +/** + * @brief + */ +void hfp_ag_answer_incoming_call(void); /** - * @brief Enable in-band ring tone + * @brief */ -void hfp_ag_set_use_in_band_ring_tone(int use_in_band_ring_tone); +void hfp_ag_join_held_call(void); + +/** + * @brief + */ +void hfp_ag_terminate_call(void); + +/* + * @brief + */ +void hfp_ag_hold_incoming_call(void); + +/* + * @brief + */ +void hfp_ag_accept_held_incoming_call(void); + +/* + * @brief + */ +void hfp_ag_reject_held_incoming_call(void); + +/* + * @brief + */ +void hfp_ag_set_microphone_gain(bd_addr_t bd_addr, int gain); + +/* + * @brief + */ +void hfp_ag_set_speaker_gain(bd_addr_t bd_addr, int gain); + +/* + * @brief + */ +void hfp_ag_set_battery_level(int level); + +/* + * @brief + */ +void hfp_ag_clear_last_dialed_number(void); + + +// Voice Recognition + +/* + * @brief + */ +void hfp_ag_activate_voice_recognition(bd_addr_t bd_addr, int activate); + +/* + * @brief + */ +void hfp_ag_send_phone_number_for_voice_tag(bd_addr_t bd_addr, const char * number); + +/* + * @brief + */ +void hfp_ag_reject_phone_number_for_voice_tag(bd_addr_t bd_addr); + + +// Cellular Actions /** * @brief @@ -209,22 +220,6 @@ void hfp_ag_outgoing_call_established(void); * @brief */ void hfp_ag_call_dropped(void); - -/** - * @brief - */ -void hfp_ag_answer_incoming_call(void); - -/** - * @brief - */ -void hfp_ag_join_held_call(void); - -/** - * @brief - */ -void hfp_ag_terminate_call(void); - /* * @brief */ @@ -240,75 +235,49 @@ void hfp_ag_set_signal_strength(int strength); */ void hfp_ag_set_roaming_status(int status); -/* - * @brief - */ -void hfp_ag_set_battery_level(int level); - - -/* - * @brief - */ -void hfp_ag_activate_voice_recognition(bd_addr_t bd_addr, int activate); - -/* - * @brief - */ -void hfp_ag_set_microphone_gain(bd_addr_t bd_addr, int gain); - -/* - * @brief - */ -void hfp_ag_set_speaker_gain(bd_addr_t bd_addr, int gain); - -/* - * @brief - */ -void hfp_ag_send_phone_number_for_voice_tag(bd_addr_t bd_addr, const char * number); - -/* - * @brief - */ -void hfp_ag_reject_phone_number_for_voice_tag(bd_addr_t bd_addr); - -/* - * @brief - */ -void hfp_ag_send_dtmf_code_done(bd_addr_t bd_addr); - /* * @brief */ void hfp_ag_set_subcriber_number_information(hfp_phone_number_t * numbers, int numbers_count); /* - * @brief + * @brief Called by cellular unit after a DTMF code was transmitted, so that the next one can be emitted */ +void hfp_ag_send_dtmf_code_done(bd_addr_t bd_addr); -void hfp_ag_send_current_call_status(bd_addr_t bd_addr, int idx); - -/* - * @brief +/** + * @brief Report Extended Audio Gateway Error result codes in the AG. + * Whenever there is an error relating to the functionality of the AG as a + * result of AT command, the AG shall send +CME ERROR: + * - +CME ERROR: 0 - AG failure + * - +CME ERROR: 1 - no connection to phone + * - +CME ERROR: 3 - operation not allowed + * - +CME ERROR: 4 - operation not supported + * - +CME ERROR: 5 - PH-SIM PIN required + * - +CME ERROR: 10 - SIM not inserted + * - +CME ERROR: 11 - SIM PIN required + * - +CME ERROR: 12 - SIM PUK required + * - +CME ERROR: 13 - SIM failure + * - +CME ERROR: 14 - SIM busy + * - +CME ERROR: 16 - incorrect password + * - +CME ERROR: 17 - SIM PIN2 required + * - +CME ERROR: 18 - SIM PUK2 required + * - +CME ERROR: 20 - memory full + * - +CME ERROR: 21 - invalid index + * - +CME ERROR: 23 - memory failure + * - +CME ERROR: 24 - text string too long + * - +CME ERROR: 25 - invalid characters in text string + * - +CME ERROR: 26 - dial string too long + * - +CME ERROR: 27 - invalid characters in dial string + * - +CME ERROR: 30 - no network service + * - +CME ERROR: 31 - network Timeout. + * - +CME ERROR: 32 - network not allowed – Emergency calls only */ -void hfp_ag_hold_incoming_call(void); - -/* - * @brief - */ -void hfp_ag_accept_held_incoming_call(void); - -/* - * @brief - */ -void hfp_ag_reject_held_incoming_call(void); - -/* - * @brief - */ -void hfp_ag_clear_last_dialed_number(void); +void hfp_ag_report_extended_audio_gateway_error_result_code(bd_addr_t bd_addr, hfp_cme_error_t error); /* API_END */ + #if defined __cplusplus } #endif diff --git a/src/classic/hfp_gsm_model.c b/src/classic/hfp_gsm_model.c index 8d1fedfb5..53169610e 100644 --- a/src/classic/hfp_gsm_model.c +++ b/src/classic/hfp_gsm_model.c @@ -71,69 +71,147 @@ static char clip_number[HFP_GSM_MAX_CALL_NUMBER_SIZE]; static char last_dialed_number[HFP_GSM_MAX_CALL_NUMBER_SIZE]; static void hfp_gsm_handler(hfp_ag_call_event_t event, uint8_t index, uint8_t type, const char * number); +static inline int get_number_active_calls(void); + +static void set_callsetup_status(hfp_callsetup_status_t status){ + callsetup_status = status; + if (callsetup_status != HFP_CALLSETUP_STATUS_OUTGOING_CALL_SETUP_IN_ALERTING_STATE) return; + + int i ; + for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){ + if (gsm_calls[i].direction == HFP_ENHANCED_CALL_DIR_OUTGOING){ + gsm_calls[i].enhanced_status = HFP_ENHANCED_CALL_STATUS_OUTGOING_ALERTING; + } + } +} + +static inline void set_enhanced_call_status_active(int index_in_table){ + gsm_calls[index_in_table].enhanced_status = HFP_ENHANCED_CALL_STATUS_ACTIVE; + gsm_calls[index_in_table].used_slot = 1; +} + +static inline void set_enhanced_call_status_held(int index_in_table){ + gsm_calls[index_in_table].enhanced_status = HFP_ENHANCED_CALL_STATUS_HELD; + gsm_calls[index_in_table].used_slot = 1; +} + +static inline void set_enhanced_call_status_response_hold(int index_in_table){ + gsm_calls[index_in_table].enhanced_status = HFP_ENHANCED_CALL_STATUS_CALL_HELD_BY_RESPONSE_AND_HOLD; + gsm_calls[index_in_table].used_slot = 1; +} + +static inline void set_enhanced_call_status_initiated(int index_in_table){ + if (gsm_calls[index_in_table].direction == HFP_ENHANCED_CALL_DIR_OUTGOING){ + gsm_calls[index_in_table].enhanced_status = HFP_ENHANCED_CALL_STATUS_OUTGOING_DIALING; + } else { + if (get_number_active_calls() > 0){ + gsm_calls[index_in_table].enhanced_status = HFP_ENHANCED_CALL_STATUS_INCOMING_WAITING; + } else { + gsm_calls[index_in_table].enhanced_status = HFP_ENHANCED_CALL_STATUS_INCOMING; + } + } + gsm_calls[index_in_table].used_slot = 1; +} + +static int get_enhanced_call_status(int index_in_table){ + if (!gsm_calls[index_in_table].used_slot) return -1; + return gsm_calls[index_in_table].enhanced_status; +} + +static inline int is_enhanced_call_status_active(int index_in_table){ + return get_enhanced_call_status(index_in_table) == HFP_ENHANCED_CALL_STATUS_ACTIVE; +} + +static inline int is_enhanced_call_status_initiated(int index_in_table){ + switch (get_enhanced_call_status(index_in_table)){ + case HFP_ENHANCED_CALL_STATUS_OUTGOING_DIALING: + case HFP_ENHANCED_CALL_STATUS_OUTGOING_ALERTING: + case HFP_ENHANCED_CALL_STATUS_INCOMING: + case HFP_ENHANCED_CALL_STATUS_INCOMING_WAITING: + return 1; + default: + return 0; + } +} + +static void free_call_slot(int index_in_table){ + gsm_calls[index_in_table].used_slot = 0; +} void hfp_gsm_init(void){ - callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS; + set_callsetup_status(HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS); clip_type = 0; memset(clip_number, 0, sizeof(clip_number)); memset(last_dialed_number, 0, sizeof(last_dialed_number)); memset(gsm_calls, 0, sizeof(gsm_calls)); int i; for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){ - gsm_calls[i].status = CALL_NONE; + free_call_slot(i); } } -static int get_number_calls_with_status(hfp_gsm_call_status_t status){ +static int get_number_calls_with_enhanced_status(hfp_enhanced_call_status_t enhanced_status){ int i, count = 0; for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){ - if (gsm_calls[i].status == status) count++; + if (get_enhanced_call_status(i) == enhanced_status) count++; } return count; } -static int get_call_index_with_status(hfp_gsm_call_status_t status){ +static int get_call_index_with_enhanced_status(hfp_enhanced_call_status_t enhanced_status){ int i ; for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){ - if (gsm_calls[i].status == status) return i; + if (get_enhanced_call_status(i) == enhanced_status) return i; + } + return -1; +} + +static inline int get_initiated_call_index(void){ + int i ; + for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){ + if (is_enhanced_call_status_initiated(i)) return i; } return -1; } static inline int get_next_free_slot(void){ - return get_call_index_with_status(CALL_NONE); + int i ; + for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){ + if (!gsm_calls[i].used_slot) return i; + } + return -1; } static inline int get_active_call_index(void){ - return get_call_index_with_status(CALL_ACTIVE); -} - -static inline int get_initiated_call_index(void){ - return get_call_index_with_status(CALL_INITIATED); + return get_call_index_with_enhanced_status(HFP_ENHANCED_CALL_STATUS_ACTIVE); } static inline int get_held_call_index(void){ - return get_call_index_with_status(CALL_HELD); + return get_call_index_with_enhanced_status(HFP_ENHANCED_CALL_STATUS_HELD); } static inline int get_response_held_call_index(void){ - return get_call_index_with_status(CALL_RESPONSE_HOLD); + return get_call_index_with_enhanced_status(HFP_ENHANCED_CALL_STATUS_CALL_HELD_BY_RESPONSE_AND_HOLD); } static inline int get_number_none_calls(void){ - return get_number_calls_with_status(CALL_NONE); + int i, count = 0; + for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){ + if (!gsm_calls[i].used_slot) count++; + } + return count; } static inline int get_number_active_calls(void){ - return get_number_calls_with_status(CALL_ACTIVE); + return get_number_calls_with_enhanced_status(HFP_ENHANCED_CALL_STATUS_ACTIVE); } static inline int get_number_held_calls(void){ - return get_number_calls_with_status(CALL_HELD); + return get_number_calls_with_enhanced_status(HFP_ENHANCED_CALL_STATUS_HELD); } static inline int get_number_response_held_calls(void){ - return get_number_calls_with_status(CALL_RESPONSE_HOLD); + return get_number_calls_with_enhanced_status(HFP_ENHANCED_CALL_STATUS_CALL_HELD_BY_RESPONSE_AND_HOLD); } static int next_call_index(void){ @@ -162,8 +240,8 @@ static void delete_call(int delete_index_in_table){ gsm_calls[i].index--; } } - - gsm_calls[delete_index_in_table].status = CALL_NONE; + free_call_slot(delete_index_in_table); + gsm_calls[delete_index_in_table].clip_type = 0; gsm_calls[delete_index_in_table].index = 0; gsm_calls[delete_index_in_table].clip_number[0] = '\0'; @@ -175,7 +253,7 @@ static void create_call(hfp_enhanced_call_dir_t direction){ int next_free_slot = get_next_free_slot(); gsm_calls[next_free_slot].direction = direction; gsm_calls[next_free_slot].index = next_call_index(); - gsm_calls[next_free_slot].status = CALL_INITIATED; + set_enhanced_call_status_initiated(next_free_slot); gsm_calls[next_free_slot].clip_type = 0; gsm_calls[next_free_slot].clip_number[0] = '\0'; gsm_calls[next_free_slot].mpty = HFP_ENHANCED_CALL_MPTY_NOT_A_CONFERENCE_CALL; @@ -201,27 +279,7 @@ hfp_gsm_call_t * hfp_gsm_call(int call_index){ for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){ hfp_gsm_call_t * call = &gsm_calls[i]; - if (call->index != call_index) continue; - - call->enhanced_status = HFP_ENHANCED_CALL_STATUS_CALL_HELD_BY_RESPONSE_AND_HOLD; - - if (call->status == CALL_ACTIVE) call->enhanced_status = HFP_ENHANCED_CALL_STATUS_ACTIVE; - if (call->status == CALL_HELD) call->enhanced_status = HFP_ENHANCED_CALL_STATUS_HELD; - - if (call->status == CALL_INITIATED){ - if (call->direction == HFP_ENHANCED_CALL_DIR_OUTGOING){ - if (callsetup_status == HFP_CALLSETUP_STATUS_OUTGOING_CALL_SETUP_IN_ALERTING_STATE){ - call->enhanced_status = HFP_ENHANCED_CALL_STATUS_OUTGOING_ALERTING; - } - call->enhanced_status = HFP_ENHANCED_CALL_STATUS_OUTGOING_DIALING; - } else { - if (get_number_active_calls() > 0){ - call->enhanced_status = HFP_ENHANCED_CALL_STATUS_INCOMING_WAITING; - } - call->enhanced_status = HFP_ENHANCED_CALL_STATUS_INCOMING; - } - } return call; } return NULL; @@ -333,14 +391,14 @@ static void hfp_gsm_handler(hfp_ag_call_event_t event, uint8_t index, uint8_t ty if (current_call_index != -1){ delete_call(current_call_index); } - callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS; + set_callsetup_status(HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS); break; case HFP_AG_OUTGOING_CALL_ACCEPTED: if (current_call_index != -1){ - gsm_calls[current_call_index].status = CALL_HELD; + set_enhanced_call_status_held(current_call_index); } - callsetup_status = HFP_CALLSETUP_STATUS_OUTGOING_CALL_SETUP_IN_DIALING_STATE; + set_callsetup_status(HFP_CALLSETUP_STATUS_OUTGOING_CALL_SETUP_IN_DIALING_STATE); break; case HFP_AG_OUTGOING_CALL_RINGING: @@ -348,27 +406,27 @@ static void hfp_gsm_handler(hfp_ag_call_event_t event, uint8_t index, uint8_t ty log_error("gsm: no active call"); return; } - callsetup_status = HFP_CALLSETUP_STATUS_OUTGOING_CALL_SETUP_IN_ALERTING_STATE; + set_callsetup_status(HFP_CALLSETUP_STATUS_OUTGOING_CALL_SETUP_IN_ALERTING_STATE); break; case HFP_AG_OUTGOING_CALL_ESTABLISHED: - callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS; - gsm_calls[initiated_call_index].status = CALL_ACTIVE; + set_callsetup_status(HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS); + set_enhanced_call_status_active(initiated_call_index); break; case HFP_AG_INCOMING_CALL: if (hfp_gsm_callsetup_status() != HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS) break; - callsetup_status = HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS; + set_callsetup_status(HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS); create_call(HFP_ENHANCED_CALL_DIR_INCOMING); break; case HFP_AG_INCOMING_CALL_ACCEPTED_BY_AG: if (hfp_gsm_callsetup_status() != HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS) break; - callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS; + set_callsetup_status(HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS); if (hfp_gsm_call_status() == HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT){ - gsm_calls[current_call_index].status = CALL_HELD; + set_enhanced_call_status_held(current_call_index); } - gsm_calls[initiated_call_index].status = CALL_ACTIVE; + set_enhanced_call_status_active(initiated_call_index); break; case HFP_AG_HELD_CALL_JOINED_BY_AG: @@ -376,14 +434,14 @@ static void hfp_gsm_handler(hfp_ag_call_event_t event, uint8_t index, uint8_t ty // TODO: is following condition correct? Can we join incoming call before it is answered? if (callsetup_status == HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS){ - gsm_calls[initiated_call_index].status = CALL_ACTIVE; - callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS; + set_enhanced_call_status_active(initiated_call_index); + set_callsetup_status(HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS); } else if (hfp_gsm_callheld_status() == HFP_CALLHELD_STATUS_CALL_ON_HOLD_OR_SWAPPED) { - gsm_calls[held_call_index].status = CALL_ACTIVE; + set_enhanced_call_status_active(held_call_index); } for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){ - if (gsm_calls[i].status == CALL_ACTIVE){ + if (is_enhanced_call_status_active(i)){ gsm_calls[i].mpty = HFP_ENHANCED_CALL_MPTY_CONFERENCE_CALL; } } @@ -392,22 +450,22 @@ static void hfp_gsm_handler(hfp_ag_call_event_t event, uint8_t index, uint8_t ty case HFP_AG_INCOMING_CALL_ACCEPTED_BY_HF: if (hfp_gsm_callsetup_status() != HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS) break; if (hfp_gsm_call_status() != HFP_CALL_STATUS_NO_HELD_OR_ACTIVE_CALLS) break; - callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS; - gsm_calls[initiated_call_index].status = CALL_ACTIVE; + set_callsetup_status(HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS); + set_enhanced_call_status_active(initiated_call_index); break; case HFP_AG_RESPONSE_AND_HOLD_ACCEPT_INCOMING_CALL_BY_AG: case HFP_AG_RESPONSE_AND_HOLD_ACCEPT_INCOMING_CALL_BY_HF: if (hfp_gsm_callsetup_status() != HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS) break; if (hfp_gsm_call_status() != HFP_CALL_STATUS_NO_HELD_OR_ACTIVE_CALLS) break; - callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS; - gsm_calls[initiated_call_index].status = CALL_RESPONSE_HOLD; + set_callsetup_status(HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS); + set_enhanced_call_status_response_hold(initiated_call_index); break; case HFP_AG_RESPONSE_AND_HOLD_ACCEPT_HELD_CALL_BY_AG: case HFP_AG_RESPONSE_AND_HOLD_ACCEPT_HELD_CALL_BY_HF: if (!hfp_gsm_response_held_active()) break; - gsm_calls[get_response_held_call_index()].status = CALL_ACTIVE; + set_enhanced_call_status_active(get_response_held_call_index()); break; case HFP_AG_RESPONSE_AND_HOLD_REJECT_HELD_CALL_BY_AG: @@ -420,7 +478,7 @@ static void hfp_gsm_handler(hfp_ag_call_event_t event, uint8_t index, uint8_t ty case HFP_AG_TERMINATE_CALL_BY_HF: switch (hfp_gsm_call_status()){ case HFP_CALL_STATUS_NO_HELD_OR_ACTIVE_CALLS: - callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS; + set_callsetup_status(HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS); break; case HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT: delete_call(current_call_index); @@ -432,10 +490,10 @@ static void hfp_gsm_handler(hfp_ag_call_event_t event, uint8_t index, uint8_t ty switch (hfp_gsm_call_status()){ case HFP_CALL_STATUS_NO_HELD_OR_ACTIVE_CALLS: if (hfp_gsm_callsetup_status() != HFP_CALLSETUP_STATUS_INCOMING_CALL_SETUP_IN_PROGRESS) break; - callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS; + set_callsetup_status(HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS); break; case HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT: - callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS; + set_callsetup_status(HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS); delete_call(current_call_index); break; default: @@ -444,7 +502,7 @@ static void hfp_gsm_handler(hfp_ag_call_event_t event, uint8_t index, uint8_t ty break; case HFP_AG_CALL_DROPPED: - callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS; + set_callsetup_status(HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS); if (hfp_gsm_call_status() != HFP_CALL_STATUS_ACTIVE_OR_HELD_CALL_IS_PRESENT) break; for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){ @@ -454,9 +512,9 @@ static void hfp_gsm_handler(hfp_ag_call_event_t event, uint8_t index, uint8_t ty case HFP_AG_CALL_HOLD_USER_BUSY: // Held or waiting call gets active, - callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS; - gsm_calls[initiated_call_index].status = CALL_NONE; - gsm_calls[held_call_index].status = CALL_ACTIVE; + set_callsetup_status(HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS); + free_call_slot(initiated_call_index); + set_enhanced_call_status_active(held_call_index); break; case HFP_AG_CALL_HOLD_RELEASE_ACTIVE_ACCEPT_HELD_OR_WAITING_CALL: @@ -469,41 +527,41 @@ static void hfp_gsm_handler(hfp_ag_call_event_t event, uint8_t index, uint8_t ty } } else { for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){ - if (gsm_calls[i].status == CALL_ACTIVE){ + if (is_enhanced_call_status_active(i)){ delete_call(i); } } } if (callsetup_status != HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS){ - gsm_calls[initiated_call_index].status = CALL_ACTIVE; + set_enhanced_call_status_active(initiated_call_index); } else { - gsm_calls[held_call_index].status = CALL_ACTIVE; + set_enhanced_call_status_active(held_call_index); } - callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS; + set_callsetup_status(HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS); break; case HFP_AG_CALL_HOLD_PARK_ACTIVE_ACCEPT_HELD_OR_WAITING_CALL: for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){ - if (gsm_calls[i].status == CALL_ACTIVE && gsm_calls[i].index != index){ - gsm_calls[i].status = CALL_HELD; + if (is_enhanced_call_status_active(i) && gsm_calls[i].index != index){ + set_enhanced_call_status_held(i); } } if (callsetup_status != HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS){ - gsm_calls[initiated_call_index].status = CALL_ACTIVE; + set_enhanced_call_status_active(initiated_call_index); } else { - gsm_calls[held_call_index].status = CALL_ACTIVE; + set_enhanced_call_status_active(held_call_index); } - callsetup_status = HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS; + set_callsetup_status(HFP_CALLSETUP_STATUS_NO_CALL_SETUP_IN_PROGRESS); break; case HFP_AG_CALL_HOLD_ADD_HELD_CALL: if (hfp_gsm_callheld_status() != HFP_CALLHELD_STATUS_NO_CALLS_HELD){ for (i = 0; i < HFP_GSM_MAX_NR_CALLS; i++){ - if (gsm_calls[i].status != CALL_NONE){ - gsm_calls[i].status = CALL_ACTIVE; + if (gsm_calls[i].used_slot){ + set_enhanced_call_status_active(i); gsm_calls[i].mpty = HFP_ENHANCED_CALL_MPTY_CONFERENCE_CALL; } } diff --git a/src/classic/hfp_gsm_model.h b/src/classic/hfp_gsm_model.h index e76f10ae3..f66d67831 100644 --- a/src/classic/hfp_gsm_model.h +++ b/src/classic/hfp_gsm_model.h @@ -54,20 +54,10 @@ extern "C" { #endif /* API_START */ - -typedef enum{ - CALL_NONE, - CALL_INITIATED, - CALL_RESPONSE_HOLD, - CALL_ACTIVE, - CALL_HELD -} hfp_gsm_call_status_t; - typedef struct { - // TODO: use enhanced_status instead of status - hfp_gsm_call_status_t status; - hfp_enhanced_call_dir_t direction; + uint8_t used_slot; hfp_enhanced_call_status_t enhanced_status; + hfp_enhanced_call_dir_t direction; hfp_enhanced_call_mode_t mode; hfp_enhanced_call_mpty_t mpty; // TODO: sort on drop call, so that index corresponds to table index diff --git a/src/classic/hfp_hf.h b/src/classic/hfp_hf.h index 785a35193..b2bde3817 100644 --- a/src/classic/hfp_hf.h +++ b/src/classic/hfp_hf.h @@ -37,13 +37,13 @@ // ***************************************************************************** // -// Minimal setup for HFP Hands-Free (HF) unit (!! UNDER DEVELOPMENT !!) +// HFP Hands-Free (HF) unit // // ***************************************************************************** -#ifndef btstack_hfp_hf_h -#define btstack_hfp_hf_h +#ifndef __BTSTACK_HFP_HF_H +#define __BTSTACK_HFP_HF_H #include "hci.h" #include "classic/sdp_query_rfcomm.h" @@ -55,7 +55,6 @@ extern "C" { /* API_START */ - /** * @brief Create HFP Hands-Free (HF) SDP service record. */ @@ -66,8 +65,11 @@ void hfp_hf_create_sdp_record(uint8_t * service, uint32_t service_record_handle, * TODO: move optional params into setters */ void hfp_hf_init(uint16_t rfcomm_channel_nr, uint32_t supported_features, uint16_t * indicators, int indicators_nr, uint32_t indicators_status); + void hfp_hf_set_codecs(uint8_t * codecs, int codecs_nr); +void hfp_hf_set_supported_features(uint32_t supported_features); + /** * @brief Register callback for the HFP Hands-Free (HF) client. */ @@ -83,7 +85,6 @@ void hfp_hf_register_packet_handler(hfp_callback_t callback); * - retrieve which HF indicators are enabled on the AG, if possible */ void hfp_hf_establish_service_level_connection(bd_addr_t bd_addr); -void hfp_hf_set_supported_features(uint32_t supported_features); /** * @brief Release the RFCOMM channel and the audio connection between the HF and the AG. @@ -95,6 +96,7 @@ void hfp_hf_release_service_level_connection(bd_addr_t bd_addr); * @brief Deactivate/reactivate status update for all indicators in the AG. */ void hfp_hf_enable_status_update_for_all_ag_indicators(bd_addr_t bd_addr); + void hfp_hf_disable_status_update_for_all_ag_indicators(bd_addr_t bd_addr); /** @@ -106,6 +108,8 @@ void hfp_hf_set_status_update_for_individual_ag_indicators(bd_addr_t bd_addr, ui /** * @brief Find out the name of the currently selected Network operator by AG. * The name is restricted to max 16 characters. + * + * TODO: what is the result of this? */ void hfp_hf_query_operator_selection(bd_addr_t bd_addr); @@ -196,7 +200,8 @@ void hfp_hf_terminate_call(bd_addr_t bd_addr); void hfp_hf_dial_number(bd_addr_t bd_addr, char * number); /** - * @brief + * @brief + * TODO: use int for number instead of string? */ void hfp_hf_dial_memory(bd_addr_t bd_addr, char * number); @@ -317,4 +322,4 @@ void hfp_hf_set_hf_indicator(bd_addr_t addr, int assigned_number, int value); } #endif -#endif \ No newline at end of file +#endif // __BTSTACK_HFP_HF_H \ No newline at end of file diff --git a/src/classic/hsp_ag.c b/src/classic/hsp_ag.c index 9ce369ee3..b934f8c71 100644 --- a/src/classic/hsp_ag.c +++ b/src/classic/hsp_ag.c @@ -37,7 +37,7 @@ // ***************************************************************************** // -// Minimal setup for HSP Audio Gateway (!! UNDER DEVELOPMENT !!) +// HSP Audio Gateway // // ***************************************************************************** @@ -60,8 +60,6 @@ #include "hsp_ag.h" #include "l2cap.h" -#define RFCOMM_SERVER_CHANNEL 1 - #define HSP_HS_BUTTON_PRESS "AT+CKPD=200" #define HSP_HS_AT_CKPD "AT+CKPD\r\n" #define HSP_AG_OK "\r\nOK\r\n" @@ -84,8 +82,6 @@ static uint16_t sco_handle = 0; static uint16_t rfcomm_handle = 0; static btstack_timer_source_t hs_timeout; -// static uint8_t connection_state = 0; - static int ag_microphone_gain = -1; static int ag_speaker_gain = -1; static uint8_t ag_ring = 0; @@ -152,7 +148,7 @@ static void emit_event_audio_connected(uint8_t status, uint16_t handle){ (*hsp_ag_callback)(event, sizeof(event)); } -void hsp_ag_create_service(uint8_t * service, uint32_t service_record_handle, int rfcomm_channel_nr, const char * name){ +void hsp_ag_create_sdp_record(uint8_t * service, uint32_t service_record_handle, int rfcomm_channel_nr, const char * name){ uint8_t* attribute; de_create_sequence(service); @@ -223,14 +219,13 @@ static int hsp_ag_send_str_over_rfcomm(uint16_t cid, char * command){ if (!rfcomm_can_send_packet_now(cid)) return 1; int err = rfcomm_send(cid, (uint8_t*) command, strlen(command)); if (err){ - printf("rfcomm_send -> error 0X%02x", err); + log_error("rfcomm_send_internal -> error 0X%02x", err); return err; } - printf("Send string: \"%s\"\n", command); return err; } -void hsp_ag_support_custom_commands(int enable){ +void hsp_ag_enable_custom_commands(int enable){ ag_support_custom_commands = enable; } @@ -301,7 +296,7 @@ void hsp_ag_disconnect(void){ void hsp_ag_set_microphone_gain(uint8_t gain){ if (gain < 0 || gain >15) { - printf("Gain must be in interval [0..15], it is given %d\n", gain); + log_error("Gain must be in interval [0..15], it is given %d", gain); return; } ag_microphone_gain = gain; @@ -311,7 +306,7 @@ void hsp_ag_set_microphone_gain(uint8_t gain){ // AG +VGS=5 [0..15] ; HS AT+VGM=6 | AG OK void hsp_ag_set_speaker_gain(uint8_t gain){ if (gain < 0 || gain >15) { - printf("Gain must be in interval [0..15], it is given %d\n", gain); + log_error("Gain must be in interval [0..15], it is given %d", gain); return; } ag_speaker_gain = gain; @@ -371,7 +366,7 @@ static void hsp_run(void){ switch (hsp_state){ case HSP_SDP_QUERY_RFCOMM_CHANNEL: hsp_state = HSP_W4_SDP_EVENT_QUERY_COMPLETE; - printf("Start SDP query %s, 0x%02x\n", bd_addr_to_str(remote), SDP_HSP); + log_info("Start SDP query %s, 0x%02x", bd_addr_to_str(remote), SDP_HSP); sdp_query_rfcomm_channel_and_name_for_uuid(remote, SDP_HSP); break; @@ -446,8 +441,8 @@ static void hsp_run(void){ } -static void packet_handler (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]); +static void packet_handler (void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ + // log_info("packet_handler type %u, packet[0] %x", packet_type, packet[0]); if (packet_type == RFCOMM_DATA_PACKET){ while (size > 0 && (packet[0] == '\n' || packet[0] == '\r')){ size--; @@ -455,7 +450,7 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack } if (strncmp((char *)packet, HSP_HS_BUTTON_PRESS, strlen(HSP_HS_BUTTON_PRESS)) == 0){ - printf("Received button press %s\n", HSP_HS_BUTTON_PRESS); + log_info("Received button press %s", HSP_HS_BUTTON_PRESS); ag_num_button_press_received++; ag_send_ok = 1; if (hsp_state == HSP_ACTIVE && ag_num_button_press_received >=2){ @@ -493,19 +488,6 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack uint16_t handle; switch (event) { - case BTSTACK_EVENT_STATE: - // BTstack activated, get started - if (packet[2] == HCI_STATE_WORKING){ - printf("BTstack activated, get started .\n"); - } - break; - - case HCI_EVENT_PIN_CODE_REQUEST: - // inform about pin code request - printf("Pin code request - using '0000'\n\r"); - reverse_bd_addr(&packet[2], event_addr); - hci_send_cmd(&hci_pin_code_request_reply, &event_addr, 4, "0000"); - break; case HCI_EVENT_SYNCHRONOUS_CONNECTION_COMPLETE:{ int index = 2; uint8_t status = packet[index++]; @@ -530,14 +512,14 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack } switch (link_type){ case 0x00: - printf("SCO Connection established. \n"); + log_info("SCO Connection established."); 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: - printf("eSCO Connection established. \n"); + log_info("eSCO Connection established."); break; default: log_error("(e)SCO reserved link_type 0x%2x", link_type); @@ -563,18 +545,17 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack reverse_bd_addr(&packet[2], event_addr); rfcomm_cid = little_endian_read_16(packet, 9); - printf("RFCOMM channel %u requested for %s\n", packet[8], bd_addr_to_str(event_addr)); + log_info("RFCOMM channel %u requested for %s", packet[8], bd_addr_to_str(event_addr)); rfcomm_accept_connection(rfcomm_cid); hsp_state = HSP_W4_RFCOMM_CONNECTED; - break; case RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE: - printf("RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE packet_handler type %u, packet[0] %x\n", packet_type, packet[0]); + log_info("RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE packet_handler type %u, packet[0] %x", packet_type, packet[0]); // data: event(8), len(8), status (8), address (48), handle(16), server channel(8), rfcomm_cid(16), max frame size(16) if (packet[2]) { - printf("RFCOMM channel open failed, status %u\n", packet[2]); + log_info("RFCOMM channel open failed, status %u§", packet[2]); hsp_ag_reset_state(); emit_event(HSP_SUBEVENT_AUDIO_CONNECTION_COMPLETE, packet[2]); } else { @@ -582,7 +563,7 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack rfcomm_handle = little_endian_read_16(packet, 9); rfcomm_cid = little_endian_read_16(packet, 12); mtu = little_endian_read_16(packet, 14); - printf("RFCOMM channel open succeeded. New RFCOMM Channel ID %u, max frame size %u, state %d\n", rfcomm_cid, mtu, hsp_state); + log_info("RFCOMM channel open succeeded. New RFCOMM Channel ID %u, max frame size %u, state %d", rfcomm_cid, mtu, hsp_state); switch (hsp_state){ case HSP_W4_RFCOMM_CONNECTED: @@ -593,19 +574,16 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack hsp_state = HSP_W2_DISCONNECT_RFCOMM; break; default: - printf("no valid state\n"); + log_error("no valid state"); break; } } break; case HCI_EVENT_DISCONNECTION_COMPLETE: - if (hsp_state != HSP_W4_SCO_DISCONNECTED){ - log_info("received gap disconnect in wrong hsp state"); - } handle = little_endian_read_16(packet,3); if (handle == sco_handle){ - printf("SCO disconnected, w2 disconnect RFCOMM\n"); + log_info("SCO disconnected, w2 disconnect RFCOMM"); sco_handle = 0; hsp_state = HSP_W2_DISCONNECT_RFCOMM; break; @@ -613,10 +591,7 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack break; case RFCOMM_EVENT_CHANNEL_CLOSED: - if (hsp_state != HSP_W4_RFCOMM_DISCONNECTED){ - log_info("received RFCOMM disconnect in wrong hsp state"); - } - printf("RFCOMM channel closed\n"); + log_info("RFCOMM channel closed"); hsp_ag_reset_state(); emit_event(HSP_SUBEVENT_AUDIO_DISCONNECTION_COMPLETE,0); break; @@ -630,17 +605,17 @@ static void handle_query_rfcomm_event(uint8_t packet_type, uint8_t *packet, uint switch (packet[0]){ case SDP_EVENT_QUERY_RFCOMM_SERVICE: channel_nr = sdp_event_query_rfcomm_service_get_rfcomm_channel(packet); - printf("** Service name: '%s', RFCOMM port %u\n", sdp_event_query_rfcomm_service_get_name(packet), channel_nr); + log_info("** Service name: '%s', RFCOMM port %u", sdp_event_query_rfcomm_service_get_name(packet), channel_nr); break; case SDP_EVENT_QUERY_COMPLETE: if (channel_nr > 0){ hsp_state = HSP_W4_RFCOMM_CONNECTED; - printf("RFCOMM create channel. state %d\n", HSP_W4_RFCOMM_CONNECTED); + log_info("RFCOMM create channel. state %d", HSP_W4_RFCOMM_CONNECTED); rfcomm_create_channel(packet_handler, remote, channel_nr, NULL); break; } hsp_ag_reset_state(); - printf("Service not found, status %u.\n", sdp_event_query_complete_get_status(packet)); + log_info("Service not found, status %u.\n", sdp_event_query_complete_get_status(packet)); if (sdp_event_query_complete_get_status(packet)){ emit_event(HSP_SUBEVENT_AUDIO_CONNECTION_COMPLETE, sdp_event_query_complete_get_status(packet)); } else { diff --git a/src/classic/hsp_ag.h b/src/classic/hsp_ag.h index d0fa3e461..8e065c003 100644 --- a/src/classic/hsp_ag.h +++ b/src/classic/hsp_ag.h @@ -37,7 +37,7 @@ // ***************************************************************************** // -// HSP Audio Gateway (!! UNDER DEVELOPMENT !!) +// HSP Audio Gateway // // ***************************************************************************** @@ -53,27 +53,36 @@ extern "C" { #endif typedef void (*hsp_ag_callback_t)(uint8_t * event, uint16_t event_size); -// Register callback (packet handler) for hsp audio gateway -void hsp_ag_register_packet_handler(hsp_ag_callback_t callback); -void hsp_ag_create_service(uint8_t * service, uint32_t service_record_handle, int rfcomm_channel_nr, const char * name); + + +void hsp_ag_create_sdp_record(uint8_t * service, int rfcomm_channel_nr, const char * name); + void hsp_ag_init(uint8_t rfcomm_channel_nr); + +// Register callback (packet handler) for hsp audio gateway +void hsp_ag_register_packet_handler(hsp_ag_callback_t callback); + void hsp_ag_connect(bd_addr_t bd_addr); + void hsp_ag_disconnect(void); // +VGM=[0..15] void hsp_ag_set_microphone_gain(uint8_t gain); + // +VGS=[0..15] void hsp_ag_set_speaker_gain(uint8_t gain); void hsp_ag_start_ringing(void); + void hsp_ag_stop_ringing(void); -void hsp_ag_support_custom_commands(int enable); - -// When support custom commands is enabled, AG will send HSP_SUBEVENT_HS_COMMAND. +// When support custom commands is enabled, AG will emit HSP_SUBEVENT_HS_COMMAND. // On occurance of this event, client's packet handler must send the result back // by calling hsp_ag_send_result function. + +void hsp_ag_enable_custom_commands(int enable); + int hsp_ag_send_result(char * result); #if defined __cplusplus diff --git a/src/classic/hsp_hs.c b/src/classic/hsp_hs.c index 473da0e32..abcba9caf 100644 --- a/src/classic/hsp_hs.c +++ b/src/classic/hsp_hs.c @@ -37,7 +37,7 @@ // ***************************************************************************** // -// Minimal setup for HSP Headset (!! UNDER DEVELOPMENT !!) +// HSP Headset // // ***************************************************************************** @@ -156,20 +156,17 @@ static int hsp_hs_send_str_over_rfcomm(uint16_t cid, const char * command){ return err; } -void hsp_hs_support_custom_indications(int enable){ +void hsp_hs_enable_custom_indications(int enable){ hs_support_custom_indications = enable; } -// When support custom commands is enabled, AG will send HSP_SUBEVENT_HS_COMMAND. -// On occurance of this event, client's packet handler must send the result back -// by calling hsp_hs_send_result function. -int hsp_hs_send_result(char * result){ +int hsp_hs_send_result(const char * result){ if (!hs_support_custom_indications) return 1; return hsp_hs_send_str_over_rfcomm(rfcomm_cid, result); } -void hsp_hs_create_service(uint8_t * service, uint32_t service_record_handle, int rfcomm_channel_nr, const char * name, uint8_t have_remote_audio_control){ +void hsp_hs_create_sdp_record(uint8_t * service, int rfcomm_channel_nr, const char * name, uint8_t have_remote_audio_control){ uint8_t* attribute; de_create_sequence(service); @@ -549,10 +546,6 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack break; case HCI_EVENT_DISCONNECTION_COMPLETE: - printf("HCI_EVENT_DISCONNECTION_COMPLETE \n"); - // if (hsp_state != HSP_W4_SCO_DISCONNECTED){ - // printf("received gap disconnect in wrong hsp state\n"); - // } handle = little_endian_read_16(packet,3); if (handle == sco_handle){ sco_handle = 0; @@ -562,10 +555,6 @@ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *pack } break; case RFCOMM_EVENT_CHANNEL_CLOSED: - printf("RFCOMM_EVENT_CHANNEL_CLOSED\n"); - // if (hsp_state != HSP_W4_RFCOMM_DISCONNECTED){ - // printf("received RFCOMM disconnect in wrong hsp state\n"); - // } printf("RFCOMM channel closed\n"); hsp_hs_reset_state(); emit_event(HSP_SUBEVENT_AUDIO_DISCONNECTION_COMPLETE,0); @@ -596,7 +585,7 @@ static void handle_query_rfcomm_event(uint8_t packet_type, uint8_t *packet, uint } } -void hsp_hs_press_button(void){ +void hsp_hs_send_button_press(void){ hs_send_button_press = 1; hsp_run(); } diff --git a/src/classic/hsp_hs.h b/src/classic/hsp_hs.h index f545d283e..ebd3b0a11 100644 --- a/src/classic/hsp_hs.h +++ b/src/classic/hsp_hs.h @@ -37,7 +37,7 @@ // ***************************************************************************** // -// HSP Headset (!! UNDER DEVELOPMENT !!) +// HSP Headset // // ***************************************************************************** @@ -52,28 +52,94 @@ extern "C" { #endif -typedef void (*hsp_hs_callback_t)(uint8_t * event, uint16_t event_size); -// Register callback (packet handler) for hsp headset -void hsp_hs_register_packet_handler(hsp_hs_callback_t callback); -void hsp_hs_create_service(uint8_t * service, uint32_t service_record_handle, int rfcomm_channel_nr, const char * name, uint8_t have_remote_audio_control); +/* API_START */ +/** + * @brief Packet handler for HSP Headset (HS) events. The HSP HS event has type HCI_EVENT_HSP_META with following subtypes: + * - HSP_SUBEVENT_ERROR + * - HSP_SUBEVENT_AUDIO_CONNECTION_COMPLETE + * - HSP_SUBEVENT_AUDIO_DISCONNECTION_COMPLETE + * - HSP_SUBEVENT_RING + * - HSP_SUBEVENT_MICROPHONE_GAIN_CHANGED + * - HSP_SUBEVENT_SPEAKER_GAIN_CHANGED + * - HSP_SUBEVENT_AG_INDICATION + */ +typedef void (*hsp_hs_callback_t)(uint8_t * event, uint16_t event_size); + +/** + * @brief Set up HSP HS + * @param rfcomm_channel_nr + */ void hsp_hs_init(uint8_t rfcomm_channel_nr); + +/** + * @brief Create HSP Headset (HS) SDP service record. have_remote_audio_control? + */ +void hsp_hs_create_sdp_record(uint8_t * service, uint32_t service_record_handle, int rfcomm_channel_nr, const char * name, uint8_t have_remote_audio_control); + +/** + * @brief Register packet handler to receive HSP HS events. + */ +void hsp_hs_register_packet_handler(hsp_hs_callback_t callback); + +/** + * @brief Connect to HSP Audio Gateway + * + * Perform SDP query for an RFCOMM service on a remote device, + * and establish an RFCOMM connection if such service is found. The reception of the + * HSP_SUBEVENT_AUDIO_CONNECTION_COMPLETE or + * HSP_SUBEVENT_AUDIO_DISCONNECTION_COMPLETE event + * indicate if the connection is successfully established or not. + */ void hsp_hs_connect(bd_addr_t bd_addr); + +/** + * @brief Disconnect from HSP Audio Gateway + * + * Releases the RFCOMM channel. + */ void hsp_hs_disconnect(bd_addr_t bd_addr); -// AT+VGM=[0..15] +/** + * @brief Set microphone gain. + * + * The new gain value will be confirmed by the HSP Audio Gateway. + * A HSP_SUBEVENT_MICROPHONE_GAIN_CHANGED event will be received. + * @param gain - valid range: [0,15] + */ void hsp_hs_set_microphone_gain(uint8_t gain); -// AT+VGS=[0..15] + +/** + * @brief Set speaker gain. + * + * The new gain value will be confirmed by the HSP Audio Gateway. + * A HSP_SUBEVENT_SPEAKER_GAIN_CHANGED event will be received. + * @param gain - valid range: [0,15] + */ void hsp_hs_set_speaker_gain(uint8_t gain); -void hsp_hs_support_custom_indications(int enable); +/** + * @brief Send button press action. + */ +void hsp_hs_send_button_press(void); -void hsp_hs_press_button(void); +/** + * @brief Enable custom indications + * + * Custom indications are disable by default. + * When enabled, custom indications are received via the HSP_SUBEVENT_AG_INDICATION. + */ +void hsp_hs_enable_custom_indications(int enable); -// When support custom commands is enabled, AG will send HSP_SUBEVENT_AG_INDICATION. -// On occurance of this event, client's packet handler must send the result back -// by calling hsp_hs_send_result function. -int hsp_hs_send_result(char * indication); +/** + * @brief Send answer to custom command + * + * On HSP_SUBEVENT_AG_INDICATION, the client needs to respond + * with this function with the result to the custom indication + */ +int hsp_hs_send_result(const char * indication); + +/* API_END */ #if defined __cplusplus } diff --git a/test/pts/hsp_ag_test.c b/test/pts/hsp_ag_test.c index e584178b2..f680ef91c 100644 --- a/test/pts/hsp_ag_test.c +++ b/test/pts/hsp_ag_test.c @@ -191,7 +191,7 @@ int btstack_main(int argc, const char * argv[]){ // init SDP, create record for SPP and register with SDP sdp_init(); memset((uint8_t *)hsp_service_buffer, 0, sizeof(hsp_service_buffer)); - hsp_ag_create_service((uint8_t *)hsp_service_buffer, 0x10003, rfcomm_channel_nr, hsp_ag_service_name); + hsp_ag_create_sdp_record((uint8_t *)hsp_service_buffer, 0x10003, rfcomm_channel_nr, hsp_ag_service_name); sdp_register_service((uint8_t *)hsp_service_buffer); // turn on! diff --git a/test/pts/hsp_hs_test.c b/test/pts/hsp_hs_test.c index 763729f45..f52e7b724 100644 --- a/test/pts/hsp_hs_test.c +++ b/test/pts/hsp_hs_test.c @@ -238,7 +238,7 @@ static int stdin_process(struct btstack_data_source *ds){ break; case 'b': printf("Press user button\n"); - hsp_hs_press_button(); + hsp_hs_send_button_press(); break; default: show_usage(); @@ -321,7 +321,7 @@ int btstack_main(int argc, const char * argv[]){ sdp_init(); memset((uint8_t *)hsp_service_buffer, 0, sizeof(hsp_service_buffer)); - hsp_hs_create_service((uint8_t *)hsp_service_buffer, 0x10004, rfcomm_channel_nr, hsp_hs_service_name, 0); + hsp_hs_create_sdp_record((uint8_t *)hsp_service_buffer, 0x10004, rfcomm_channel_nr, hsp_hs_service_name, 0); sdp_register_service((uint8_t *)hsp_service_buffer); hci_discoverable_control(1);