diff --git a/src/classic/hfp.c b/src/classic/hfp.c index 9d914517a..ebc65b4f3 100644 --- a/src/classic/hfp.c +++ b/src/classic/hfp.c @@ -993,6 +993,7 @@ void hfp_parse(hfp_connection_t * context, uint8_t byte, int isHandsFree){ context->command = HFP_CMD_RETRIEVE_AG_INDICATORS_STATUS; break; case HFP_W4_RETRIEVE_INDICATORS: + context->send_ag_indicators_segment = 0; context->command = HFP_CMD_RETRIEVE_AG_INDICATORS; break; default: diff --git a/src/classic/hfp.h b/src/classic/hfp.h index 69f3ead7f..1fd67140c 100644 --- a/src/classic/hfp.h +++ b/src/classic/hfp.h @@ -540,6 +540,7 @@ typedef struct hfp_connection { uint8_t send_phone_number_for_voice_tag; uint8_t send_ag_status_indicators; + uint8_t send_ag_indicators_segment; uint8_t send_response_and_hold_status; // 0 - don't send. BRTH:0 == 1, .. // AG only diff --git a/src/classic/hfp_ag.c b/src/classic/hfp_ag.c index 2ce69b221..9cfc117bc 100644 --- a/src/classic/hfp_ag.c +++ b/src/classic/hfp_ag.c @@ -256,23 +256,82 @@ static int hfp_ag_report_extended_audio_gateway_error(uint16_t cid, uint8_t erro return send_str_over_rfcomm(cid, buffer); } -static int hfp_ag_indicators_join(char * buffer, int buffer_size, hfp_connection_t * context){ - if (buffer_size < get_hfp_ag_indicators_nr(context) * (1 + sizeof(hfp_ag_indicator_t))) return 0; - int i; - int offset = 0; - for (i = 0; i < get_hfp_ag_indicators_nr(context)-1; i++) { - offset += snprintf(buffer+offset, buffer_size-offset, "(\"%s\",(%d,%d)),", +// fast & small implementation for fixed int size +static int string_len_for_uint32(uint32_t i){ + if (i < 10) return 1; + if (i < 100) return 2; + if (i < 1000) return 3; + if (i < 10000) return 4; + if (i < 10000) return 5; + if (i < 1000000) return 6; + if (i < 10000000) return 7; + if (i < 100000000) return 8; + if (i < 1000000000) return 9; + return 10; +} + +// get size for indicator string +static int hfp_ag_indicators_string_size(hfp_connection_t * context, int i){ + // template: ("$NAME",($MIN,$MAX)) + return 8 + strlen(get_hfp_ag_indicators(context)[i].name) + + string_len_for_uint32(get_hfp_ag_indicators(context)[i].min_range) + + string_len_for_uint32(get_hfp_ag_indicators(context)[i].min_range); +} + +// store indicator +static void hfp_ag_indicators_string_store(hfp_connection * context, int i, uint8_t * buffer){ + sprintf((char *) buffer, "(\"%s\",(%d,%d)),", get_hfp_ag_indicators(context)[i].name, get_hfp_ag_indicators(context)[i].min_range, get_hfp_ag_indicators(context)[i].max_range); +} + +// structure: header [indicator [comma indicator]] footer +static int hfp_ag_indicators_cmd_generator_num_segments(hfp_connection_t * context){ + int num_indicators = get_hfp_ag_indicators_nr(context); + if (!num_indicators) return 2; + return 3 + (num_indicators-1) * 2; +} + +// get size of individual segment for hfp_ag_retrieve_indicators_cmd +static int hfp_ag_indicators_cmd_generator_get_segment_len(hfp_connection_t * context, int index){ + if (index == 0) { + return strlen(HFP_INDICATOR) + 3; // "\n\r%s:"" } - if ( i < get_hfp_ag_indicators_nr(context)){ - offset += snprintf(buffer+offset, buffer_size-offset, "(\"%s\",(%d,%d))", - get_hfp_ag_indicators(context)[i].name, - get_hfp_ag_indicators(context)[i].min_range, - get_hfp_ag_indicators(context)[i].max_range); + index--; + int num_indicators = get_hfp_ag_indicators_nr(context); + int indicator_index = index >> 1; + if ((index & 1) == 0){ + return hfp_ag_indicators_string_size(context, indicator_index); } - return offset; + if (indicator_index == num_indicators - 1){ + return 8; // "\r\n\r\nOK\r\n" + } + return 1; // comma +} + +static void hgp_ag_indicators_cmd_generator_store_segment(hfp_connection_t * context, int index, uint8_t * buffer){ + if (index == 0){ + *buffer++ = '\n'; + *buffer++ = '\r'; + int len = strlen(HFP_INDICATOR); + memcpy(buffer, HFP_INDICATOR, len); + buffer += len; + *buffer++ = ':'; + return; + } + index--; + int num_indicators = get_hfp_ag_indicators_nr(context); + int indicator_index = index >> 1; + if ((index & 1) == 0){ + hfp_ag_indicators_string_store(context, indicator_index, buffer); + return; + } + if (indicator_index == num_indicators-1){ + memcpy(buffer, "\r\n\r\nOK\r\n", 8); + return; + } + *buffer = ','; } static int hfp_hf_indicators_join(char * buffer, int buffer_size){ @@ -324,16 +383,35 @@ static int hfp_ag_call_services_join(char * buffer, int buffer_size){ return offset; } -static int hfp_ag_retrieve_indicators_cmd(uint16_t cid, hfp_connection_t * context){ - char buffer[250]; - int offset = snprintf(buffer, sizeof(buffer), "\r\n%s:", HFP_INDICATOR); - offset += hfp_ag_indicators_join(buffer+offset, sizeof(buffer)-offset, context); - - buffer[offset] = 0; - - offset += snprintf(buffer+offset, sizeof(buffer)-offset, "\r\n\r\nOK\r\n"); - buffer[offset] = 0; - return send_str_over_rfcomm(cid, buffer); +static int hfp_ag_cmd_via_generator(uint16_t cid, hfp_connection_t * context, + int start_segment, int num_segments, + int (*get_segment_len)(hfp_connection_t * context, int segment), + void (*store_segment) (hfp_connection_t * context, int segment, uint8_t * buffer)){ + + // assumes: can send now == true + // assumes: num segments > 0 + rfcomm_reserve_packet_buffer(); + int mtu = rfcomm_get_max_frame_size(cid); + uint8_t * data = rfcomm_get_outgoing_buffer(); + int offset = 0; + int segment = start_segment; + while (segment < num_segments){ + int segment_len = get_segment_len(context, segment); + if (offset + segment_len <= mtu){ + store_segment(context, segment, data+offset); + offset += segment_len; + segment++; + } + } + rfcomm_send_prepared(cid, offset); + return segment; +} + +// returns next segment to store +static int hfp_ag_retrieve_indicators_cmd_via_generator(uint16_t cid, hfp_connection_t * context, int start_segment){ + int num_segments = hfp_ag_indicators_cmd_generator_num_segments(context); + return hfp_ag_cmd_via_generator(cid, context, start_segment, num_segments, + hfp_ag_indicators_cmd_generator_get_segment_len, hgp_ag_indicators_cmd_generator_store_segment); } static int hfp_ag_retrieve_indicators_status_cmd(uint16_t cid){ @@ -355,7 +433,7 @@ static int hfp_ag_set_indicator_status_update_cmd(uint16_t cid, uint8_t activate static int hfp_ag_retrieve_can_hold_call_cmd(uint16_t cid){ - char buffer[100]; + char buffer[40]; int offset = snprintf(buffer, sizeof(buffer), "\r\n%s:", HFP_SUPPORT_CALL_HOLD_AND_MULTIPARTY_SERVICES); offset += hfp_ag_call_services_join(buffer+offset, sizeof(buffer)-offset); @@ -586,10 +664,21 @@ static int hfp_ag_run_for_context_service_level_connection(hfp_connection_t * co return done; case HFP_CMD_RETRIEVE_AG_INDICATORS: - if (context->state != HFP_W4_RETRIEVE_INDICATORS) break; - context->state = HFP_W4_RETRIEVE_INDICATORS_STATUS; - hfp_ag_retrieve_indicators_cmd(context->rfcomm_cid, context); - return 1; + if (context->state == HFP_W4_RETRIEVE_INDICATORS) { + context->command = HFP_CMD_NONE; // prevent reentrance + int next_segment = hfp_ag_retrieve_indicators_cmd_via_generator(context->rfcomm_cid, context, context->send_ag_indicators_segment); + if (next_segment < hfp_ag_indicators_cmd_generator_num_segments(context)){ + // prepare sending of next segment + context->send_ag_indicators_segment = next_segment; + context->command = HFP_CMD_RETRIEVE_AG_INDICATORS; + } else { + // done, go to next state + context->send_ag_indicators_segment = 0; + context->state = HFP_W4_RETRIEVE_INDICATORS_STATUS; + } + return 1; + } + break; case HFP_CMD_RETRIEVE_AG_INDICATORS_STATUS: if (context->state != HFP_W4_RETRIEVE_INDICATORS_STATUS) break; @@ -1477,7 +1566,7 @@ static void hfp_run_for_context(hfp_connection_t *context){ if (context->send_ag_status_indicators){ context->send_ag_status_indicators = 0; - hfp_ag_retrieve_indicators_cmd(context->rfcomm_cid, context); + hfp_ag_retrieve_indicators_status_cmd(context->rfcomm_cid); return; } @@ -1593,6 +1682,7 @@ static void hfp_handle_rfcomm_data(uint8_t packet_type, uint16_t channel, uint8_ case HFP_CMD_RETRIEVE_AG_INDICATORS_STATUS: // expected by SLC state machine if (context->state < HFP_SERVICE_LEVEL_CONNECTION_ESTABLISHED) break; + context->send_ag_indicators_segment = 0; context->send_ag_status_indicators = 1; break; case HFP_CMD_LIST_CURRENT_CALLS: diff --git a/test/hfp/mock.c b/test/hfp/mock.c index 5db6d1c7a..89d1cfb65 100644 --- a/test/hfp/mock.c +++ b/test/hfp/mock.c @@ -66,6 +66,8 @@ static uint16_t rfcomm_payload_len = 0; static uint8_t outgoing_rfcomm_payload[200]; static uint16_t outgoing_rfcomm_payload_len = 0; +static uint8_t rfcomm_reserved_buffer[1000]; + void * active_connection; hfp_connection_t * hfp_context; @@ -109,7 +111,8 @@ char * get_next_hfp_command(int start_command_offset, int end_command_offset){ return NULL; } -static void print_without_newlines(uint8_t *data, uint16_t len){ +void print_without_newlines(uint8_t *data, uint16_t len); +void print_without_newlines(uint8_t *data, uint16_t len){ int found_newline = 0; int found_item = 0; @@ -131,7 +134,6 @@ extern "C" void l2cap_init(void){} extern "C" void l2cap_register_packet_handler(void (*handler)(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size)){ } - int rfcomm_send_internal(uint16_t rfcomm_cid, uint8_t *data, uint16_t len){ int start_command_offset = 2; int end_command_offset = 2; @@ -155,6 +157,21 @@ int rfcomm_send_internal(uint16_t rfcomm_cid, uint8_t *data, uint16_t len){ return 0; } +int rfcomm_reserve_packet_buffer(void){ + return 1; +}; +void rfcomm_release_packet_buffer(void){}; +uint8_t * rfcomm_get_outgoing_buffer(void) { + return rfcomm_reserved_buffer; +} +uint16_t rfcomm_get_max_frame_size(uint16_t rfcomm_cid){ + return sizeof(rfcomm_reserved_buffer); +} +int rfcomm_send_prepared(uint16_t rfcomm_cid, uint16_t len){ + printf("--- rfcomm_send_prepared with len %u ---\n", len); + return rfcomm_send_internal(rfcomm_cid, rfcomm_reserved_buffer, len); +} + static void hci_event_sco_complete(){ uint8_t event[19]; uint8_t pos = 0;