From 62ee15abfc1c7a63617117e8e29e2c028a1e8186 Mon Sep 17 00:00:00 2001 From: Zhenhua Zhang Date: Wed, 4 Nov 2009 21:12:32 +0800 Subject: [PATCH 4/4] Fix: Fill in the phone number info for outgoing call There're two cases of outgoing call: dial from HF or dial from phone. In the first case, some phones may not support enhanced call status (AT+CLCC) to query outgoing number. So we need to pass struct atd_cb_data to fill in the number and type. Later, we invoke AT+CLCC to query phone number and overwrite ours if we are wrong. In the second case, the phone is dialing from phone and we know nothing. So we have to query it through AT+CLCC when callsetup=2. If phone does not support AT+CLCC, we fake a new call for it. Update: change algorithm in sync_dialing_cb to handle multi calls situation. --- drivers/hfpmodem/voicecall.c | 211 +++++++++++++++++++++++++++++++++++------- 1 files changed, 176 insertions(+), 35 deletions(-) diff --git a/drivers/hfpmodem/voicecall.c b/drivers/hfpmodem/voicecall.c index dee7787..830bdbb 100644 --- a/drivers/hfpmodem/voicecall.c +++ b/drivers/hfpmodem/voicecall.c @@ -49,6 +49,7 @@ static const char *none_prefix[] = { NULL }; static const char *chld_prefix[] = { "+CHLD:", NULL }; +static const char *clcc_prefix[] = { "+CLCC:", NULL }; struct voicecall_data { GAtChat *chat; @@ -76,6 +77,13 @@ struct change_state_req { int affected_types; }; +struct atd_cb_data { + void *cb; + void *data; + void *user; + struct ofono_phone_number ph; +}; + static struct ofono_call *create_call(struct voicecall_data *d, int type, int direction, int status, const char *num, int num_type, int clip) @@ -108,6 +116,73 @@ static struct ofono_call *create_call(struct voicecall_data *d, int type, return call; } +static void new_call_notify(struct ofono_voicecall *vc, int type, + int direction, int status, + const char *num, int num_type, int clip) +{ + struct voicecall_data *vd = ofono_voicecall_get_data(vc); + struct ofono_call *c; + + c = create_call(vd, type, direction, status, num, num_type, clip); + + ofono_voicecall_notify(vc, c); +} + +static GSList *parse_clcc(GAtResult *result) +{ + GAtResultIter iter; + GSList *l = NULL; + int id, dir, status, type; + struct ofono_call *call; + + g_at_result_iter_init(&iter, result); + + while (g_at_result_iter_next(&iter, "+CLCC:")) { + const char *str = ""; + int number_type = 129; + + if (!g_at_result_iter_next_number(&iter, &id)) + continue; + + if (!g_at_result_iter_next_number(&iter, &dir)) + continue; + + if (!g_at_result_iter_next_number(&iter, &status)) + continue; + + if (!g_at_result_iter_next_number(&iter, &type)) + continue; + + if (!g_at_result_iter_skip_next(&iter)) + continue; + + if (g_at_result_iter_next_string(&iter, &str)) + g_at_result_iter_next_number(&iter, &number_type); + + call = g_try_new0(struct ofono_call, 1); + + if (!call) + break; + + call->id = id; + call->direction = dir; + call->status = status; + call->type = type; + strncpy(call->phone_number.number, str, + OFONO_MAX_PHONE_NUMBER_LENGTH); + call->phone_number.type = number_type; + + if (strlen(call->phone_number.number) > 0) + call->clip_validity = 0; + else + call->clip_validity = 2; + + l = g_slist_insert_sorted(l, call, at_util_call_compare); + } + + return l; +} + static void generic_cb(gboolean ok, GAtResult *result, gpointer user_data) { struct change_state_req *req = user_data; @@ -134,12 +209,11 @@ static void generic_cb(gboolean ok, GAtResult *result, gpointer user_data) static void atd_cb(gboolean ok, GAtResult *result, gpointer user_data) { - struct cb_data *cbd = user_data; + struct atd_cb_data *cbd = user_data; struct ofono_voicecall *vc = cbd->user; struct voicecall_data *vd = ofono_voicecall_get_data(vc); ofono_voicecall_cb_t cb = cbd->cb; - int type = 128; - int validity = 2; + struct ofono_phone_number ph = cbd->ph; struct ofono_error error; struct ofono_call *call; @@ -150,7 +224,8 @@ static void atd_cb(gboolean ok, GAtResult *result, gpointer user_data) if (!ok) goto out; - call = create_call(vd, 0, 0, CALL_STATUS_DIALING, NULL, type, validity); + call = create_call(vd, 0, 0, CALL_STATUS_DIALING, + ph.number, ph.type, 0); if (!call) { ofono_error("Unable to allocate call, " @@ -168,13 +243,20 @@ static void hfp_dial(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) { struct voicecall_data *vd = ofono_voicecall_get_data(vc); - struct cb_data *cbd = cb_data_new(cb, data); + struct atd_cb_data *cbd; char buf[256]; + cbd = g_try_new0(struct atd_cb_data, 1); + if (!cbd) goto error; + cbd->cb = cb; + cbd->data = data; cbd->user = vc; + + memcpy(&cbd->ph, ph, sizeof(struct ofono_phone_number)); + if (ph->type == 145) sprintf(buf, "ATD+%s", ph->number); else @@ -351,23 +433,71 @@ static void ciev_call_notify(struct ofono_voicecall *vc, { struct voicecall_data *vd = ofono_voicecall_get_data(vc); - if (g_slist_length(vd->calls) == 1) { - switch (value) { - case 0: - release_call(vc, call); - break; - case 1: - call->status = CALL_STATUS_ACTIVE; - ofono_voicecall_notify(vc, call); - break; - default: - break; - } + switch (value) { + case 0: + release_call(vc, call); + break; + case 1: + call->status = CALL_STATUS_ACTIVE; + ofono_voicecall_notify(vc, call); + break; + default: + break; } vd->cind_val[HFP_INDICATOR_CALL] = value; } +static void sync_dialing_cb(gboolean ok, GAtResult *result, gpointer user_data) +{ + struct ofono_voicecall *vc = user_data; + struct voicecall_data *vd = ofono_voicecall_get_data(vc); + struct ofono_error error; + GSList *calls = NULL; + GSList *l = NULL; + struct ofono_call *nc = NULL; + struct ofono_call *oc = vd->call; + + dump_response("sync_dialing_cb", ok, result); + decode_at_error(&error, g_at_result_final_response(result)); + + if (!ok) + return; + + calls = parse_clcc(result); + + if (calls == NULL) + return; + + while (calls) { + nc = calls->data; + + if (vd->calls) + l = g_slist_find_custom(vd->calls, nc, + at_util_call_compare); + + if (l) { + oc = l->data; + + if (memcmp(nc, oc, sizeof(struct ofono_call))) { + ofono_voicecall_notify(vc, nc); + + memcpy(oc, nc, sizeof(struct ofono_call)); + } + + } else + new_call_notify(vc, nc->type, nc->direction, nc->status, + nc->phone_number.number, + nc->phone_number.type, + nc->clip_validity); + + calls = calls->next; + } + + g_slist_foreach(calls, (GFunc) g_free, NULL); + g_slist_free(calls); +} + static void ciev_callsetup_notify(struct ofono_voicecall *vc, struct ofono_call *call, unsigned int value) @@ -376,24 +506,35 @@ static void ciev_callsetup_notify(struct ofono_voicecall *vc, unsigned int ciev_callsetup = vd->cind_val[HFP_INDICATOR_CALLSETUP]; unsigned int ciev_call = vd->cind_val[HFP_INDICATOR_CALL]; - if (g_slist_length(vd->calls) == 1) { - switch (value) { - case 0: - /* call=0 and callsetup=1: reject an incoming call - * call=0 and callsetup=2,3: interrupt an outgoing call - */ - if ((ciev_call == 0) && (ciev_callsetup > 0)) - release_call(vc, call); - break; - case 1: - case 2: - break; - case 3: - call->status = CALL_STATUS_ALERTING; - ofono_voicecall_notify(vc, call); - default: - break; - } + switch (value) { + case 0: + /* call=0 and callsetup=1: reject an incoming call + * call=0 and callsetup=2,3: interrupt an outgoing call + */ + if ((ciev_call == 0) && (ciev_callsetup > 0)) + release_call(vc, call); + break; + case 1: + break; + case 2: + /* two cases of outgoing call: dial from HF or AG. + * from HF: query and sync the phone number. + * from AG: query and create call. + * if phone does not support CLLC, we guess the call. + */ + if (vd->ag_features & AG_FEATURE_ENHANCED_CALL_STATUS) + g_at_chat_send(vd->chat, "AT+CLCC", clcc_prefix, + sync_dialing_cb, + vc, NULL); + else if (!vd->call) + new_call_notify(vc, 0, 0, CALL_STATUS_DIALING, + NULL, 128, 2); + break; + case 3: + call->status = CALL_STATUS_ALERTING; + ofono_voicecall_notify(vc, call); + default: + break; } vd->cind_val[HFP_INDICATOR_CALLSETUP] = value; -- 1.6.2.5