summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorZhenhua Zhang <zhenhua.zhang@intel.com>2009-11-13 19:28:32 +0800
committerDenis Kenzior <denkenz@gmail.com>2009-11-13 11:15:13 -0600
commite620a058fb5a2287011c93a3bcdea2aa5f7e1b0a (patch)
tree4f7260d5b4599130b06b1f64815c6f52d2d81690
parent6c428c9823d41e60793ddff93c842c612c85450f (diff)
downloadofono-e620a058fb5a2287011c93a3bcdea2aa5f7e1b0a.tar.bz2
Fix: Fill in the phone number info for outgoing call
There're two cases of outgoing call: dial from HF or dial from phone. We could receive callsetup=2 indicator in both case. So adding AT+CLCC query to sync the outgoing call. In the first case, we only need to sync the phone number. In the second case, the phone is dialing from phone and we know nothing. Create a new call and notify the core. If phone does not support AT+CLCC and there's no call dialing from HF, we fake a new call for it.
-rw-r--r--drivers/hfpmodem/voicecall.c153
1 files changed, 123 insertions, 30 deletions
diff --git a/drivers/hfpmodem/voicecall.c b/drivers/hfpmodem/voicecall.c
index 9ba72e46..8ac49d74 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;
@@ -108,6 +109,20 @@ static struct ofono_call *create_call(struct voicecall_data *d, int type,
return call;
}
+static struct ofono_call *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);
+
+ return c;
+}
+
static void generic_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct change_state_req *req = user_data;
@@ -351,23 +366,78 @@ 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;
+ unsigned int call_held = vd->cind_val[HFP_INDICATOR_CALLHELD];
+
+ dump_response("sync_dialing_cb", ok, result);
+ decode_at_error(&error, g_at_result_final_response(result));
+
+ if (!ok)
+ return;
+
+ calls = at_util_parse_clcc(result);
+
+ if (calls == NULL)
+ return;
+
+ if (oc && call_held == 0) {
+ l = g_slist_find_custom(calls, oc, at_util_call_compare);
+
+ if (l) {
+ nc = l->data;
+
+ if (memcmp(nc, oc, sizeof(struct ofono_call))) {
+ ofono_voicecall_notify(vc, nc);
+
+ memcpy(oc, nc, sizeof(struct ofono_call));
+ }
+ }
+ } else {
+ while (calls) {
+ nc = calls->data;
+
+ if (vd->calls)
+ l = g_slist_find_custom(vd->calls, nc,
+ at_util_call_compare);
+
+ if (!l)
+ 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,29 +446,50 @@ 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)
+ 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;
}
+static void ciev_callheld_notify(struct ofono_voicecall *vc,
+ struct ofono_call *call,
+ unsigned int value)
+{
+ struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+
+ vd->cind_val[HFP_INDICATOR_CALLHELD] = value;
+}
+
static void ciev_notify(GAtResult *result, gpointer user_data)
{
struct ofono_voicecall *vc = user_data;
@@ -423,6 +514,8 @@ static void ciev_notify(GAtResult *result, gpointer user_data)
ciev_call_notify(vc, call, value);
else if (index == vd->cind_pos[HFP_INDICATOR_CALLSETUP])
ciev_callsetup_notify(vc, call, value);
+ else if (index == vd->cind_pos[HFP_INDICATOR_CALLHELD])
+ ciev_callheld_notify(vc, call, value);
}
static void chld_cb(gboolean ok, GAtResult *result, gpointer user_data)