hfp: use generator to split ag indicators list into multiple segements

This commit is contained in:
Matthias Ringwald 2015-12-18 15:11:08 +01:00
parent 5b69ff6c7f
commit fbe69a2b49
3 changed files with 118 additions and 4 deletions

View File

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

View File

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

View File

@ -256,6 +256,84 @@ static int hfp_ag_report_extended_audio_gateway_error(uint16_t cid, uint8_t erro
return send_str_over_rfcomm(cid, buffer);
}
// 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:""
}
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);
}
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_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;
@ -336,6 +414,28 @@ static int hfp_ag_retrieve_indicators_cmd(uint16_t cid, hfp_connection_t * conte
return send_str_over_rfcomm(cid, buffer);
}
// returns next segment to store
static int hfp_ag_retrieve_indicators_cmd_via_generator(uint16_t cid, hfp_connection_t * context, int start_segment){
// 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;
int num_segments = hfp_ag_indicators_cmd_generator_num_segments(context);
while (segment < num_segments){
int segment_len = hfp_ag_indicators_cmd_generator_get_segment_len(context, segment);
if (offset + segment_len <= mtu){
hgp_ag_indicators_cmd_generator_store_segment(context, segment, data+offset);
offset += segment_len;
segment++;
}
}
rfcomm_send_prepared(cid, offset);
return segment;
}
static int hfp_ag_retrieve_indicators_status_cmd(uint16_t cid){
char buffer[40];
int offset = snprintf(buffer, sizeof(buffer), "\r\n%s:", HFP_INDICATOR);
@ -586,10 +686,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;
@ -1593,6 +1704,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: