summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPekka Pessi <Pekka.Pessi@nokia.com>2009-10-09 17:45:37 +0300
committerAki Niemi <aki.niemi@nokia.com>2009-10-16 11:18:52 +0300
commit86b6cd91c94d714e9353b0532f801dee349ce40e (patch)
tree71a5276c3047e6bc1e2f86996d8e31f0de0e74a5
parent74cc9cb4b7450184743c0f972650a4c48b77d223 (diff)
downloadofono-86b6cd91c94d714e9353b0532f801dee349ce40e.tar.bz2
isimodem/voicecall.c: initial version
Implementing all the voicecall methods. Tested with Nokia 2008 modems. - problems in call creation are probably not reported properly - deflect not implemented in modem, not properly tested TODO: Clean up style issues and align with other drivers.
-rw-r--r--Makefile.am2
-rw-r--r--drivers/isimodem/isi-call-debug.c345
-rw-r--r--drivers/isimodem/isi-call.h421
-rw-r--r--drivers/isimodem/voicecall.c1277
-rw-r--r--gisi/client.c11
5 files changed, 2013 insertions, 43 deletions
diff --git a/Makefile.am b/Makefile.am
index 483c958f..99dcd150 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -77,6 +77,8 @@ builtin_sources += $(gisi_sources) \
drivers/isimodem/devinfo.c \
drivers/isimodem/network-registration.c \
drivers/isimodem/voicecall.c \
+ drivers/isimodem/isi-call.h \
+ drivers/isimodem/isi-call-debug.c \
drivers/isimodem/sms.c \
drivers/isimodem/cbs.c \
drivers/isimodem/sim.c \
diff --git a/drivers/isimodem/isi-call-debug.c b/drivers/isimodem/isi-call-debug.c
new file mode 100644
index 00000000..5d6483c4
--- /dev/null
+++ b/drivers/isimodem/isi-call-debug.c
@@ -0,0 +1,345 @@
+/*
+ * This file is part of oFono - Open Source Telephony
+ *
+ * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * Contact: <Pekka.Pessi@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <gisi/client.h>
+#include <gisi/iter.h>
+
+#include "isi-call.h"
+
+#include <ofono/log.h>
+
+#define DUMP(fmt, arg...) ofono_debug(fmt, ## arg)
+
+char const *isi_call_status_name(enum isi_call_status value)
+{
+ switch (value) {
+#define _(X) case CALL_STATUS_ ## X: return #X
+ _(IDLE);
+ _(CREATE);
+ _(COMING);
+ _(PROCEEDING);
+ _(MO_ALERTING);
+ _(MT_ALERTING);
+ _(WAITING);
+ _(ANSWERED);
+ _(ACTIVE);
+ _(MO_RELEASE);
+ _(MT_RELEASE);
+ _(HOLD_INITIATED);
+ _(HOLD);
+ _(RETRIEVE_INITIATED);
+ _(RECONNECT_PENDING);
+ _(TERMINATED);
+ _(SWAP_INITIATED);
+#undef _
+ }
+ return "<UNKNOWN>";
+}
+
+char const *isi_call_message_id_name(enum isi_call_message_id value)
+{
+ switch (value) {
+#define _(X) case X: return #X
+ _(CALL_CREATE_REQ);
+ _(CALL_CREATE_RESP);
+ _(CALL_COMING_IND);
+ _(CALL_MO_ALERT_IND);
+ _(CALL_MT_ALERT_IND);
+ _(CALL_WAITING_IND);
+ _(CALL_ANSWER_REQ);
+ _(CALL_ANSWER_RESP);
+ _(CALL_RELEASE_REQ);
+ _(CALL_RELEASE_RESP);
+ _(CALL_RELEASE_IND);
+ _(CALL_TERMINATED_IND);
+ _(CALL_STATUS_REQ);
+ _(CALL_STATUS_RESP);
+ _(CALL_STATUS_IND);
+ _(CALL_SERVER_STATUS_IND);
+ _(CALL_CONTROL_REQ);
+ _(CALL_CONTROL_RESP);
+ _(CALL_CONTROL_IND);
+ _(CALL_MODE_SWITCH_REQ);
+ _(CALL_MODE_SWITCH_RESP);
+ _(CALL_MODE_SWITCH_IND);
+ _(CALL_DTMF_SEND_REQ);
+ _(CALL_DTMF_SEND_RESP);
+ _(CALL_DTMF_STOP_REQ);
+ _(CALL_DTMF_STOP_RESP);
+ _(CALL_DTMF_STATUS_IND);
+ _(CALL_DTMF_TONE_IND);
+ _(CALL_RECONNECT_IND);
+ _(CALL_PROPERTY_GET_REQ);
+ _(CALL_PROPERTY_GET_RESP);
+ _(CALL_PROPERTY_SET_REQ);
+ _(CALL_PROPERTY_SET_RESP);
+ _(CALL_PROPERTY_SET_IND);
+ _(CALL_EMERGENCY_NBR_CHECK_REQ);
+ _(CALL_EMERGENCY_NBR_CHECK_RESP);
+ _(CALL_EMERGENCY_NBR_GET_REQ);
+ _(CALL_EMERGENCY_NBR_GET_RESP);
+ _(CALL_EMERGENCY_NBR_MODIFY_REQ);
+ _(CALL_EMERGENCY_NBR_MODIFY_RESP);
+ _(CALL_GSM_NOTIFICATION_IND);
+ _(CALL_GSM_USER_TO_USER_REQ);
+ _(CALL_GSM_USER_TO_USER_RESP);
+ _(CALL_GSM_USER_TO_USER_IND);
+ _(CALL_GSM_BLACKLIST_CLEAR_REQ);
+ _(CALL_GSM_BLACKLIST_CLEAR_RESP);
+ _(CALL_GSM_BLACKLIST_TIMER_IND);
+ _(CALL_GSM_DATA_CH_INFO_IND);
+ _(CALL_GSM_CCP_GET_REQ);
+ _(CALL_GSM_CCP_GET_RESP);
+ _(CALL_GSM_CCP_CHECK_REQ);
+ _(CALL_GSM_CCP_CHECK_RESP);
+ _(CALL_GSM_COMING_REJ_IND);
+ _(CALL_GSM_RAB_IND);
+ _(CALL_GSM_IMMEDIATE_MODIFY_IND);
+ _(CALL_CREATE_NO_SIMATK_REQ);
+ _(CALL_GSM_SS_DATA_IND);
+ _(CALL_TIMER_REQ);
+ _(CALL_TIMER_RESP);
+ _(CALL_TIMER_NTF);
+ _(CALL_TIMER_IND);
+ _(CALL_TIMER_RESET_REQ);
+ _(CALL_TIMER_RESET_RESP);
+ _(CALL_EMERGENCY_NBR_IND);
+ _(CALL_SERVICE_DENIED_IND);
+ _(CALL_RELEASE_END_REQ);
+ _(CALL_RELEASE_END_RESP);
+ _(CALL_USER_CONNECT_IND);
+ _(CALL_AUDIO_CONNECT_IND);
+ _(CALL_KODIAK_ALLOW_CTRL_REQ);
+ _(CALL_KODIAK_ALLOW_CTRL_RESP);
+ _(CALL_SERVICE_ACTIVATE_IND);
+ _(CALL_SERVICE_ACTIVATE_REQ);
+ _(CALL_SERVICE_ACTIVATE_RESP);
+ _(CALL_SIM_ATK_IND);
+ _(CALL_CONTROL_OPER_IND);
+ _(CALL_TEST_CALL_STATUS_IND);
+ _(CALL_SIM_ATK_INFO_IND);
+ _(CALL_SECURITY_IND);
+ _(CALL_MEDIA_HANDLE_REQ);
+ _(CALL_MEDIA_HANDLE_RESP);
+ _(COMMON_MESSAGE);
+#undef _
+ }
+ return "<UNKNOWN>";
+}
+
+char const *isi_call_isi_cause_name(enum isi_call_isi_cause value)
+{
+ switch (value)
+ {
+#define _(X) case CALL_CAUSE_ ## X: return "CAUSE_" #X
+ _(NO_CAUSE);
+ _(NO_CALL);
+ _(TIMEOUT);
+ _(RELEASE_BY_USER);
+ _(BUSY_USER_REQUEST);
+ _(ERROR_REQUEST);
+ _(COST_LIMIT_REACHED);
+ _(CALL_ACTIVE);
+ _(NO_CALL_ACTIVE);
+ _(INVALID_CALL_MODE);
+ _(SIGNALLING_FAILURE);
+ _(TOO_LONG_ADDRESS);
+ _(INVALID_ADDRESS);
+ _(EMERGENCY);
+ _(NO_TRAFFIC_CHANNEL);
+ _(NO_COVERAGE);
+ _(CODE_REQUIRED);
+ _(NOT_ALLOWED);
+ _(NO_DTMF);
+ _(CHANNEL_LOSS);
+ _(FDN_NOT_OK);
+ _(USER_TERMINATED);
+ _(BLACKLIST_BLOCKED);
+ _(BLACKLIST_DELAYED);
+ _(NUMBER_NOT_FOUND);
+ _(NUMBER_CANNOT_REMOVE);
+ _(EMERGENCY_FAILURE);
+ _(CS_SUSPENDED);
+ _(DCM_DRIVE_MODE);
+ _(MULTIMEDIA_NOT_ALLOWED);
+ _(SIM_REJECTED);
+ _(NO_SIM);
+ _(SIM_LOCK_OPERATIVE);
+ _(SIMATKCC_REJECTED);
+ _(SIMATKCC_MODIFIED);
+ _(DTMF_INVALID_DIGIT);
+ _(DTMF_SEND_ONGOING);
+ _(CS_INACTIVE);
+ _(SECURITY_MODE);
+ _(TRACFONE_FAILED);
+ _(TRACFONE_WAIT_FAILED);
+ _(TRACFONE_CONF_FAILED);
+ _(TEMPERATURE_LIMIT);
+ _(KODIAK_POC_FAILED);
+ _(NOT_REGISTERED);
+ _(CS_CALLS_ONLY);
+ _(VOIP_CALLS_ONLY);
+ _(LIMITED_CALL_ACTIVE);
+ _(LIMITED_CALL_NOT_ALLOWED);
+ _(SECURE_CALL_NOT_POSSIBLE);
+ _(INTERCEPT);
+#undef _
+ }
+ return "<UNKNOWN>";
+}
+
+char const *isi_call_gsm_cause_name(enum isi_call_gsm_cause value)
+{
+ switch (value)
+ {
+#define _(X) case CALL_GSM_CAUSE_ ## X: return "GSM_CAUSE_" #X
+ _(UNASSIGNED_NUMBER);
+ _(NO_ROUTE);
+ _(CH_UNACCEPTABLE);
+ _(OPER_BARRING);
+ _(NORMAL);
+ _(USER_BUSY);
+ _(NO_USER_RESPONSE);
+ _(ALERT_NO_ANSWER);
+ _(CALL_REJECTED);
+ _(NUMBER_CHANGED);
+ _(NON_SELECT_CLEAR);
+ _(DEST_OUT_OF_ORDER);
+ _(INVALID_NUMBER);
+ _(FACILITY_REJECTED);
+ _(RESP_TO_STATUS);
+ _(NORMAL_UNSPECIFIED);
+ _(NO_CHANNEL);
+ _(NETW_OUT_OF_ORDER);
+ _(TEMPORARY_FAILURE);
+ _(CONGESTION);
+ _(ACCESS_INFO_DISC);
+ _(CHANNEL_NA);
+ _(RESOURCES_NA);
+ _(QOS_NA);
+ _(FACILITY_UNSUBS);
+ _(COMING_BARRED_CUG);
+ _(BC_UNAUTHORIZED);
+ _(BC_NA);
+ _(SERVICE_NA);
+ _(BEARER_NOT_IMPL);
+ _(ACM_MAX);
+ _(FACILITY_NOT_IMPL);
+ _(ONLY_RDI_BC);
+ _(SERVICE_NOT_IMPL);
+ _(INVALID_TI);
+ _(NOT_IN_CUG);
+ _(INCOMPATIBLE_DEST);
+ _(INV_TRANS_NET_SEL);
+ _(SEMANTICAL_ERR);
+ _(INVALID_MANDATORY);
+ _(MSG_TYPE_INEXIST);
+ _(MSG_TYPE_INCOMPAT);
+ _(IE_NON_EXISTENT);
+ _(COND_IE_ERROR);
+ _(MSG_INCOMPATIBLE);
+ _(TIMER_EXPIRY);
+ _(PROTOCOL_ERROR);
+ _(INTERWORKING);
+#undef _
+ }
+ return "<UNKNOWN>";
+}
+
+char const *isi_call_cause_name(uint8_t cause_type, uint8_t cause)
+{
+ switch (cause_type)
+ {
+ case CALL_CAUSE_TYPE_DEFAULT:
+ case CALL_CAUSE_TYPE_CLIENT:
+ case CALL_CAUSE_TYPE_SERVER:
+ return isi_call_isi_cause_name(cause);
+ case CALL_CAUSE_TYPE_NETWORK:
+ return isi_call_gsm_cause_name(cause);
+ }
+ return "<UNKNOWN>";
+}
+
+static void isi_call_hex_dump(uint8_t const m[],
+ size_t len,
+ char const *name)
+{
+ char const *prefix;
+ char hex[3 * 16 + 1];
+ char ascii[16 + 1];
+ size_t i, j, k;
+
+ if (strncmp(name, "CALL_", 5))
+ prefix = "CALL ";
+ else
+ prefix = "";
+
+ DUMP("%s%s [%s=0x%02X len=%zu]:",
+ prefix, name, "message_id", m[1], len);
+
+ strcpy(hex, " **"), j = 3;
+ strcpy(ascii, "."), k = 1;
+
+ for (i = 1; i < len; i++) {
+ sprintf(hex + j, " %02X", m[i]), j += 3;
+ ascii[k++] = g_ascii_isgraph(m[i]) ? m[i] : '.';
+
+ if ((i & 15) == 15) {
+ DUMP(" *%-48s : %.*s", hex, (int)k, ascii);
+ j = 0, k = 0;
+ }
+ }
+
+ if (j) {
+ DUMP(" *%-48s : %.*s", hex, (int)k, ascii);
+ }
+}
+
+void isi_call_debug(const void *restrict buf, size_t len, void *data)
+{
+ uint8_t const *m = buf;
+ char const *name;
+
+ m = buf, m--, len++, buf = m;
+
+ if (len < 4) {
+ DUMP("CALL: %s [len=%zu]", "RUNT", len);
+ return;
+ }
+
+ name = isi_call_message_id_name(m[1]);
+
+ isi_call_hex_dump(m, len, name);
+}
diff --git a/drivers/isimodem/isi-call.h b/drivers/isimodem/isi-call.h
new file mode 100644
index 00000000..98f7583f
--- /dev/null
+++ b/drivers/isimodem/isi-call.h
@@ -0,0 +1,421 @@
+/*
+ * This file is part of oFono - Open Source Telephony
+ *
+ * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * Contact: <Pekka.Pessi@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef __GISI_CALL_H
+#define __GISI_CALL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+#define PN_CALL 0x01
+
+enum isi_call_message_id {
+ CALL_CREATE_REQ = 0x01,
+ CALL_CREATE_RESP = 0x02,
+ CALL_COMING_IND = 0x03,
+ CALL_MO_ALERT_IND = 0x04,
+ CALL_MT_ALERT_IND = 0x05,
+ CALL_WAITING_IND = 0x06,
+ CALL_ANSWER_REQ = 0x07,
+ CALL_ANSWER_RESP = 0x08,
+ CALL_RELEASE_REQ = 0x09,
+ CALL_RELEASE_RESP = 0x0A,
+ CALL_RELEASE_IND = 0x0B,
+ CALL_TERMINATED_IND = 0x0C,
+ CALL_STATUS_REQ = 0x0D,
+ CALL_STATUS_RESP = 0x0E,
+ CALL_STATUS_IND = 0x0F,
+ CALL_SERVER_STATUS_IND = 0x10,
+ CALL_CONTROL_REQ = 0x11,
+ CALL_CONTROL_RESP = 0x12,
+ CALL_CONTROL_IND = 0x13,
+ CALL_MODE_SWITCH_REQ = 0x14,
+ CALL_MODE_SWITCH_RESP = 0x15,
+ CALL_MODE_SWITCH_IND = 0x16,
+ CALL_DTMF_SEND_REQ = 0x17,
+ CALL_DTMF_SEND_RESP = 0x18,
+ CALL_DTMF_STOP_REQ = 0x19,
+ CALL_DTMF_STOP_RESP = 0x1A,
+ CALL_DTMF_STATUS_IND = 0x1B,
+ CALL_DTMF_TONE_IND = 0x1C,
+ CALL_RECONNECT_IND = 0x1E,
+ CALL_PROPERTY_GET_REQ = 0x1F,
+ CALL_PROPERTY_GET_RESP = 0x20,
+ CALL_PROPERTY_SET_REQ = 0x21,
+ CALL_PROPERTY_SET_RESP = 0x22,
+ CALL_PROPERTY_SET_IND = 0x23,
+ CALL_EMERGENCY_NBR_CHECK_REQ = 0x28,
+ CALL_EMERGENCY_NBR_CHECK_RESP = 0x29,
+ CALL_EMERGENCY_NBR_GET_REQ = 0x26,
+ CALL_EMERGENCY_NBR_GET_RESP = 0x27,
+ CALL_EMERGENCY_NBR_MODIFY_REQ = 0x24,
+ CALL_EMERGENCY_NBR_MODIFY_RESP = 0x25,
+ CALL_GSM_NOTIFICATION_IND = 0xA0,
+ CALL_GSM_USER_TO_USER_REQ = 0xA1,
+ CALL_GSM_USER_TO_USER_RESP = 0xA2,
+ CALL_GSM_USER_TO_USER_IND = 0xA3,
+ CALL_GSM_BLACKLIST_CLEAR_REQ = 0xA4,
+ CALL_GSM_BLACKLIST_CLEAR_RESP = 0xA5,
+ CALL_GSM_BLACKLIST_TIMER_IND = 0xA6,
+ CALL_GSM_DATA_CH_INFO_IND = 0xA7,
+ CALL_GSM_CCP_GET_REQ = 0xAA,
+ CALL_GSM_CCP_GET_RESP = 0xAB,
+ CALL_GSM_CCP_CHECK_REQ = 0xAC,
+ CALL_GSM_CCP_CHECK_RESP = 0xAD,
+ CALL_GSM_COMING_REJ_IND = 0xA9,
+ CALL_GSM_RAB_IND = 0xA8,
+ CALL_GSM_IMMEDIATE_MODIFY_IND = 0xAE,
+ CALL_CREATE_NO_SIMATK_REQ = 0x2A,
+ CALL_GSM_SS_DATA_IND = 0xAF,
+ CALL_TIMER_REQ = 0x2B,
+ CALL_TIMER_RESP = 0x2C,
+ CALL_TIMER_NTF = 0x2D,
+ CALL_TIMER_IND = 0x2E,
+ CALL_TIMER_RESET_REQ = 0x2F,
+ CALL_TIMER_RESET_RESP = 0x30,
+ CALL_EMERGENCY_NBR_IND = 0x31,
+ CALL_SERVICE_DENIED_IND = 0x32,
+ CALL_RELEASE_END_REQ = 0x34,
+ CALL_RELEASE_END_RESP = 0x35,
+ CALL_USER_CONNECT_IND = 0x33,
+ CALL_AUDIO_CONNECT_IND = 0x40,
+ CALL_KODIAK_ALLOW_CTRL_REQ = 0x36,
+ CALL_KODIAK_ALLOW_CTRL_RESP = 0x37,
+ CALL_SERVICE_ACTIVATE_IND = 0x38,
+ CALL_SERVICE_ACTIVATE_REQ = 0x39,
+ CALL_SERVICE_ACTIVATE_RESP = 0x3A,
+ CALL_SIM_ATK_IND = 0x3B,
+ CALL_CONTROL_OPER_IND = 0x3C,
+ CALL_TEST_CALL_STATUS_IND = 0x3E,
+ CALL_SIM_ATK_INFO_IND = 0x3F,
+ CALL_SECURITY_IND = 0x41,
+ CALL_MEDIA_HANDLE_REQ = 0x42,
+ CALL_MEDIA_HANDLE_RESP = 0x43,
+ COMMON_MESSAGE = 0xF0,
+};
+
+enum isi_call_status {
+ CALL_STATUS_IDLE = 0x00,
+ CALL_STATUS_CREATE = 0x01,
+ CALL_STATUS_COMING = 0x02,
+ CALL_STATUS_PROCEEDING = 0x03,
+ CALL_STATUS_MO_ALERTING = 0x04,
+ CALL_STATUS_MT_ALERTING = 0x05,
+ CALL_STATUS_WAITING = 0x06,
+ CALL_STATUS_ANSWERED = 0x07,
+ CALL_STATUS_ACTIVE = 0x08,
+ CALL_STATUS_MO_RELEASE = 0x09,
+ CALL_STATUS_MT_RELEASE = 0x0A,
+ CALL_STATUS_HOLD_INITIATED = 0x0B,
+ CALL_STATUS_HOLD = 0x0C,
+ CALL_STATUS_RETRIEVE_INITIATED = 0x0D,
+ CALL_STATUS_RECONNECT_PENDING = 0x0E,
+ CALL_STATUS_TERMINATED = 0x0F,
+ CALL_STATUS_SWAP_INITIATED = 0x10,
+};
+
+enum isi_call_isi_cause {
+ CALL_CAUSE_NO_CAUSE = 0x00,
+ CALL_CAUSE_NO_CALL = 0x01,
+ CALL_CAUSE_TIMEOUT = 0x02,
+ CALL_CAUSE_RELEASE_BY_USER = 0x03,
+ CALL_CAUSE_BUSY_USER_REQUEST = 0x04,
+ CALL_CAUSE_ERROR_REQUEST = 0x05,
+ CALL_CAUSE_COST_LIMIT_REACHED = 0x06,
+ CALL_CAUSE_CALL_ACTIVE = 0x07,
+ CALL_CAUSE_NO_CALL_ACTIVE = 0x08,
+ CALL_CAUSE_INVALID_CALL_MODE = 0x09,
+ CALL_CAUSE_SIGNALLING_FAILURE = 0x0A,
+ CALL_CAUSE_TOO_LONG_ADDRESS = 0x0B,
+ CALL_CAUSE_INVALID_ADDRESS = 0x0C,
+ CALL_CAUSE_EMERGENCY = 0x0D,
+ CALL_CAUSE_NO_TRAFFIC_CHANNEL = 0x0E,
+ CALL_CAUSE_NO_COVERAGE = 0x0F,
+ CALL_CAUSE_CODE_REQUIRED = 0x10,
+ CALL_CAUSE_NOT_ALLOWED = 0x11,
+ CALL_CAUSE_NO_DTMF = 0x12,
+ CALL_CAUSE_CHANNEL_LOSS = 0x13,
+ CALL_CAUSE_FDN_NOT_OK = 0x14,
+ CALL_CAUSE_USER_TERMINATED = 0x15,
+ CALL_CAUSE_BLACKLIST_BLOCKED = 0x16,
+ CALL_CAUSE_BLACKLIST_DELAYED = 0x17,
+ CALL_CAUSE_NUMBER_NOT_FOUND = 0x18,
+ CALL_CAUSE_NUMBER_CANNOT_REMOVE = 0x19,
+ CALL_CAUSE_EMERGENCY_FAILURE = 0x1A,
+ CALL_CAUSE_CS_SUSPENDED = 0x1B,
+ CALL_CAUSE_DCM_DRIVE_MODE = 0x1C,
+ CALL_CAUSE_MULTIMEDIA_NOT_ALLOWED = 0x1D,
+ CALL_CAUSE_SIM_REJECTED = 0x1E,
+ CALL_CAUSE_NO_SIM = 0x1F,
+ CALL_CAUSE_SIM_LOCK_OPERATIVE = 0x20,
+ CALL_CAUSE_SIMATKCC_REJECTED = 0x21,
+ CALL_CAUSE_SIMATKCC_MODIFIED = 0x22,
+ CALL_CAUSE_DTMF_INVALID_DIGIT = 0x23,
+ CALL_CAUSE_DTMF_SEND_ONGOING = 0x24,
+ CALL_CAUSE_CS_INACTIVE = 0x25,
+ CALL_CAUSE_SECURITY_MODE = 0x26,
+ CALL_CAUSE_TRACFONE_FAILED = 0x27,
+ CALL_CAUSE_TRACFONE_WAIT_FAILED = 0x28,
+ CALL_CAUSE_TRACFONE_CONF_FAILED = 0x29,
+ CALL_CAUSE_TEMPERATURE_LIMIT = 0x2A,
+ CALL_CAUSE_KODIAK_POC_FAILED = 0x2B,
+ CALL_CAUSE_NOT_REGISTERED = 0x2C,
+ CALL_CAUSE_CS_CALLS_ONLY = 0x2D,
+ CALL_CAUSE_VOIP_CALLS_ONLY = 0x2E,
+ CALL_CAUSE_LIMITED_CALL_ACTIVE = 0x2F,
+ CALL_CAUSE_LIMITED_CALL_NOT_ALLOWED = 0x30,
+ CALL_CAUSE_SECURE_CALL_NOT_POSSIBLE = 0x31,
+ CALL_CAUSE_INTERCEPT = 0x32,
+};
+
+enum isi_call_gsm_cause {
+ CALL_GSM_CAUSE_UNASSIGNED_NUMBER = 0x01,
+ CALL_GSM_CAUSE_NO_ROUTE = 0x03,
+ CALL_GSM_CAUSE_CH_UNACCEPTABLE = 0x06,
+ CALL_GSM_CAUSE_OPER_BARRING = 0x08,
+ CALL_GSM_CAUSE_NORMAL = 0x10,
+ CALL_GSM_CAUSE_USER_BUSY = 0x11,
+ CALL_GSM_CAUSE_NO_USER_RESPONSE = 0x12,
+ CALL_GSM_CAUSE_ALERT_NO_ANSWER = 0x13,
+ CALL_GSM_CAUSE_CALL_REJECTED = 0x15,
+ CALL_GSM_CAUSE_NUMBER_CHANGED = 0x16,
+ CALL_GSM_CAUSE_NON_SELECT_CLEAR = 0x1A,
+ CALL_GSM_CAUSE_DEST_OUT_OF_ORDER = 0x1B,
+ CALL_GSM_CAUSE_INVALID_NUMBER = 0x1C,
+ CALL_GSM_CAUSE_FACILITY_REJECTED = 0x1D,
+ CALL_GSM_CAUSE_RESP_TO_STATUS = 0x1E,
+ CALL_GSM_CAUSE_NORMAL_UNSPECIFIED = 0x1F,
+ CALL_GSM_CAUSE_NO_CHANNEL = 0x22,
+ CALL_GSM_CAUSE_NETW_OUT_OF_ORDER = 0x26,
+ CALL_GSM_CAUSE_TEMPORARY_FAILURE = 0x29,
+ CALL_GSM_CAUSE_CONGESTION = 0x2A,
+ CALL_GSM_CAUSE_ACCESS_INFO_DISC = 0x2B,
+ CALL_GSM_CAUSE_CHANNEL_NA = 0x2C,
+ CALL_GSM_CAUSE_RESOURCES_NA = 0x2F,
+ CALL_GSM_CAUSE_QOS_NA = 0x31,
+ CALL_GSM_CAUSE_FACILITY_UNSUBS = 0x32,
+ CALL_GSM_CAUSE_COMING_BARRED_CUG = 0x37,
+ CALL_GSM_CAUSE_BC_UNAUTHORIZED = 0x39,
+ CALL_GSM_CAUSE_BC_NA = 0x3A,
+ CALL_GSM_CAUSE_SERVICE_NA = 0x3F,
+ CALL_GSM_CAUSE_BEARER_NOT_IMPL = 0x41,
+ CALL_GSM_CAUSE_ACM_MAX = 0x44,
+ CALL_GSM_CAUSE_FACILITY_NOT_IMPL = 0x45,
+ CALL_GSM_CAUSE_ONLY_RDI_BC = 0x46,
+ CALL_GSM_CAUSE_SERVICE_NOT_IMPL = 0x4F,
+ CALL_GSM_CAUSE_INVALID_TI = 0x51,
+ CALL_GSM_CAUSE_NOT_IN_CUG = 0x57,
+ CALL_GSM_CAUSE_INCOMPATIBLE_DEST = 0x58,
+ CALL_GSM_CAUSE_INV_TRANS_NET_SEL = 0x5B,
+ CALL_GSM_CAUSE_SEMANTICAL_ERR = 0x5F,
+ CALL_GSM_CAUSE_INVALID_MANDATORY = 0x60,
+ CALL_GSM_CAUSE_MSG_TYPE_INEXIST = 0x61,
+ CALL_GSM_CAUSE_MSG_TYPE_INCOMPAT = 0x62,
+ CALL_GSM_CAUSE_IE_NON_EXISTENT = 0x63,
+ CALL_GSM_CAUSE_COND_IE_ERROR = 0x64,
+ CALL_GSM_CAUSE_MSG_INCOMPATIBLE = 0x65,
+ CALL_GSM_CAUSE_TIMER_EXPIRY = 0x66,
+ CALL_GSM_CAUSE_PROTOCOL_ERROR = 0x6F,
+ CALL_GSM_CAUSE_INTERWORKING = 0x7F,
+};
+
+enum isi_call_cause_type {
+ CALL_CAUSE_TYPE_DEFAULT = 0x00,
+ CALL_CAUSE_TYPE_CLIENT = 0x01,
+ CALL_CAUSE_TYPE_SERVER = 0x02,
+ CALL_CAUSE_TYPE_NETWORK = 0x03,
+};
+
+enum isi_call_subblock {
+ CALL_ORIGIN_ADDRESS = 0x01,
+ CALL_ORIGIN_SUBADDRESS = 0x02,
+ CALL_DESTINATION_ADDRESS = 0x03,
+ CALL_DESTINATION_SUBADDRESS = 0x04,
+ CALL_DESTINATION_PRE_ADDRESS = 0x05,
+ CALL_DESTINATION_POST_ADDRESS = 0x06,
+ CALL_MODE = 0x07,
+ CALL_CAUSE = 0x08,
+ CALL_OPERATION = 0x09,
+ CALL_STATUS = 0x0A,
+ CALL_STATUS_INFO = 0x0B,
+ CALL_ALERTING_INFO = 0x0C,
+ CALL_RELEASE_INFO = 0x0D,
+ CALL_ORIGIN_INFO = 0x0E,
+ CALL_DTMF_DIGIT = 0x0F,
+ CALL_DTMF_STRING = 0x10,
+ CALL_DTMF_BCD_STRING = 0x19,
+ CALL_DTMF_INFO = 0x1A,
+ CALL_PROPERTY_INFO = 0x13,
+ CALL_EMERGENCY_NUMBER = 0x14,
+ CALL_DTMF_STATUS = 0x11,
+ CALL_DTMF_TONE = 0x12,
+ CALL_GSM_CUG_INFO = 0xA0,
+ CALL_GSM_ALERTING_PATTERN = 0xA1,
+ CALL_GSM_DEFLECTION_ADDRESS = 0xA2,
+ CALL_GSM_DEFLECTION_SUBADDRESS = 0xA3,
+ CALL_GSM_REDIRECTING_ADDRESS = 0xA4,
+ CALL_GSM_REDIRECTING_SUBADDRESS = 0xA5,
+ CALL_GSM_REMOTE_ADDRESS = 0xA6,
+ CALL_GSM_REMOTE_SUBADDRESS = 0xA7,
+ CALL_GSM_USER_TO_USER_INFO = 0xA8,
+ CALL_GSM_DIAGNOSTICS = 0xA9,
+ CALL_GSM_SS_DIAGNOSTICS = 0xAA,
+ CALL_GSM_NEW_DESTINATION = 0xAB,
+ CALL_GSM_CCBS_INFO = 0xAC,
+ CALL_GSM_ADDRESS_OF_B = 0xAD,
+ CALL_GSM_SUBADDRESS_OF_B = 0xB0,
+ CALL_GSM_NOTIFY = 0xB1,
+ CALL_GSM_SS_NOTIFY = 0xB2,
+ CALL_GSM_SS_CODE = 0xB3,
+ CALL_GSM_SS_STATUS = 0xB4,
+ CALL_GSM_SS_NOTIFY_INDICATOR = 0xB5,
+ CALL_GSM_SS_HOLD_INDICATOR = 0xB6,
+ CALL_GSM_SS_ECT_INDICATOR = 0xB7,
+ CALL_GSM_DATA_CH_INFO = 0xB8,
+ CALL_DESTINATION_CS_ADDRESS = 0x16,
+ CALL_GSM_CCP = 0xBA,
+ CALL_GSM_RAB_INFO = 0xB9,
+ CALL_GSM_FNUR_INFO = 0xBB,
+ CALL_GSM_CAUSE_OF_NO_CLI = 0xBC,
+ CALL_GSM_MM_CAUSE = 0xBD,
+ CALL_GSM_EVENT_INFO = 0xBE,
+ CALL_GSM_DETAILED_CAUSE = 0xBF,
+ CALL_GSM_SS_DATA = 0xC0,
+ CALL_TIMER = 0x17,
+ CALL_GSM_ALS_INFO = 0xC1,
+ CALL_STATE_AUTO_CHANGE = 0x18,
+ CALL_EMERGENCY_NUMBER_INFO = 0x1B,
+ CALL_STATUS_MODE = 0x1C,
+ CALL_ADDR_AND_STATUS_INFO = 0x1D,
+ CALL_DTMF_TIMERS = 0x1E,
+ CALL_NAS_SYNC_INDICATOR = 0x1F,
+ CALL_NW_CAUSE = 0x20,
+ CALL_TRACFONE_RESULT = 0x21,
+ CALL_KODIAK_POC = 0x22,
+ CALL_DISPLAY_NUMBER = 0x23,
+ CALL_DESTINATION_URI = 0x24,
+ CALL_ORIGIN_URI = 0x25,
+ CALL_URI = 0x26,
+ CALL_SYSTEM_INFO = 0x27,
+ CALL_SYSTEMS = 0x28,
+ CALL_VOIP_TIMER = 0x29,
+ CALL_REDIRECTING_URI = 0x2A,
+ CALL_REMOTE_URI = 0x2B,
+ CALL_DEFLECTION_URI = 0x2C,
+ CALL_TRANSFER_INFO = 0x2D,
+ CALL_FORWARDING_INFO = 0x2E,
+ CALL_ID_INFO = 0x2F,
+ CALL_TEST_CALL = 0x30,
+ CALL_AUDIO_CONF_INFO = 0x31,
+ CALL_SECURITY_INFO = 0x33,
+ CALL_SINGLE_TIMERS = 0x32,
+ CALL_MEDIA_INFO = 0x35,
+ CALL_MEDIA_HANDLE = 0x34,
+ CALL_MODE_CHANGE_INFO = 0x36,
+ CALL_ADDITIONAL_PARAMS = 0x37,
+ CALL_DSAC_INFO = 0x38,
+};
+
+enum isi_call_id {
+ CALL_ID_NONE = 0x00,
+ CALL_ID_1 = 0x01,
+ CALL_ID_2 = 0x02,
+ CALL_ID_3 = 0x03,
+ CALL_ID_4 = 0x04,
+ CALL_ID_5 = 0x05,
+ CALL_ID_6 = 0x06,
+ CALL_ID_7 = 0x07,
+ CALL_ID_CONFERENCE = 0x10,
+ CALL_ID_WAITING = 0x20,
+ CALL_ID_HOLD = 0x40,
+ CALL_ID_ACTIVE = 0x80,
+ CALL_ID_ALL = 0xF0,
+};
+
+enum isi_call_mode {
+ CALL_MODE_EMERGENCY = 0x00,
+ CALL_MODE_SPEECH = 0x01,
+ CALL_GSM_MODE_ALS_LINE_1 = 0xA5,
+ CALL_GSM_MODE_ALS_LINE_2 = 0xA2,
+};
+
+enum {
+ CALL_MODE_INFO_NONE = 0,
+ CALL_MODE_ORIGINATOR = 0x01,
+};
+
+enum {
+ CALL_PRESENTATION_ALLOWED = 0x00,
+ CALL_PRESENTATION_RESTRICTED = 0x01,
+ CALL_GSM_PRESENTATION_DEFAULT = 0x07,
+};
+
+enum isi_call_operation {
+ CALL_OP_HOLD = 0x01,
+ CALL_OP_RETRIEVE = 0x02,
+ CALL_OP_SWAP = 0x03,
+ CALL_OP_CONFERENCE_BUILD = 0x04,
+ CALL_OP_CONFERENCE_SPLIT = 0x05,
+ CALL_OP_DATA_RATE_CHANGE = 0x06,
+ CALL_GSM_OP_CUG = 0xA0,
+ CALL_GSM_OP_TRANSFER = 0xA1,
+ CALL_GSM_OP_DEFLECT = 0xA2,
+ CALL_GSM_OP_CCBS = 0xA3,
+ CALL_GSM_OP_UUS1 = 0xA4,
+ CALL_GSM_OP_UUS2 = 0xA5,
+ CALL_GSM_OP_UUS3 = 0xA6,
+};
+
+enum {
+ CALL_GSM_OP_UUS_REQUIRED = 0x01,
+};
+
+enum call_status_mode {
+ CALL_STATUS_MODE_DEFAULT = 0x00,
+ CALL_STATUS_MODE_ADDR = 0x01,
+ CALL_STATUS_MODE_ADDR_AND_ORIGIN = 0x02,
+ CALL_STATUS_MODE_POC = 0x03,
+ CALL_STATUS_MODE_VOIP_ADDR = 0x04,
+};
+
+enum {
+ CALL_DTMF_ENABLE_TONE_IND_SEND = 0x01,
+ CALL_DTMF_DISABLE_TONE_IND_SEND = 0x02,
+};
+
+char const *isi_call_cause_name(uint8_t cause_type, uint8_t cause);
+char const *isi_call_gsm_cause_name(enum isi_call_gsm_cause value);
+char const *isi_call_isi_cause_name(enum isi_call_isi_cause value);
+char const *isi_call_status_name(enum isi_call_status value);
+char const *isi_call_message_id_name(enum isi_call_message_id value);
+
+void isi_call_debug(const void *restrict buf, size_t len, void *data);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif
diff --git a/drivers/isimodem/voicecall.c b/drivers/isimodem/voicecall.c
index bb24a801..390914b7 100644
--- a/drivers/isimodem/voicecall.c
+++ b/drivers/isimodem/voicecall.c
@@ -3,7 +3,7 @@
*
* Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
*
- * Contact: Aki Niemi <aki.niemi@nokia.com>
+ * Contact: Pekka Pessi <Pekka.Pessi@nokia.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -30,124 +30,1331 @@
#include <stdlib.h>
#include <string.h>
#include <errno.h>
+#include <assert.h>
#include <glib.h>
#include <gisi/netlink.h>
#include <gisi/client.h>
+#include <gisi/iter.h>
#include <ofono/log.h>
#include <ofono/modem.h>
#include <ofono/voicecall.h>
#include "isi.h"
+#include "isi-call.h"
-#define PN_CALL 0x01
+struct isi_call {
+ uint8_t id, call_id, status, mode, mode_info, cause_type, cause;
+ uint8_t addr_type, presentation;
+ uint8_t reason;
+ char address[20], addr_pad[4];
+};
-struct voicecall_data {
+struct isi_voicecall {
GIsiClient *client;
+
+ struct isi_call_req_context *queue;
+
struct isi_version version;
+ struct isi_call calls[8];
};
-static void isi_dial(struct ofono_voicecall *vc,
- const struct ofono_phone_number *number,
- enum ofono_clir_option clir, enum ofono_cug_option cug,
- ofono_voicecall_cb_t cb, void *data)
+/* ------------------------------------------------------------------------- */
+
+static void isi_call_notify(struct ofono_voicecall *ovc,
+ struct isi_call *call);
+static void isi_call_release(struct ofono_voicecall *, struct isi_call *);
+static struct ofono_call isi_call_as_ofono_call(struct isi_call const *);
+static int isi_call_status_to_clcc(struct isi_call const *call);
+static struct isi_call *isi_call_set_idle(struct isi_call *call);
+
+typedef void GIsiIndication (GIsiClient *client,
+ const void *restrict data, size_t len,
+ uint16_t object, void *opaque);
+
+typedef void GIsiVerify (GIsiClient *client, bool alive, void *opaque);
+
+typedef bool GIsiResponse(GIsiClient *client,
+ void const * restrict data, size_t len,
+ uint16_t object, void *opaque);
+
+static GIsiVerify isi_call_verify_cb;
+static gboolean isi_call_register(gpointer);
+
+enum {
+ ISI_CALL_TIMEOUT = 1000,
+};
+
+/* ------------------------------------------------------------------------- */
+/* Request context for voicecall cb */
+
+struct isi_call_req_context;
+
+typedef void isi_call_req_step(struct isi_call_req_context *, int reason);
+
+struct isi_call_req_context {
+ struct isi_call_req_context *next, **prev;
+ isi_call_req_step *step;
+ struct ofono_voicecall *ovc;
+ ofono_voicecall_cb_t cb;
+ void *data;
+};
+
+static struct isi_call_req_context *
+isi_call_req(struct ofono_voicecall *ovc,
+ void const * restrict req,
+ size_t len,
+ GIsiResponse *handler,
+ ofono_voicecall_cb_t cb,
+ void *data)
{
+ struct isi_voicecall *ivc;
+ struct isi_call_req_context *irc;
+
+ ivc = ofono_voicecall_get_data(ovc);
+
+ irc = g_try_new0(struct isi_call_req_context, 1);
+
+ if (irc) {
+ irc->ovc = ovc;
+ irc->cb = cb;
+ irc->data = data;
+
+ if (g_isi_request_make(ivc->client, req, len,
+ ISI_CALL_TIMEOUT, handler, irc))
+ return irc;
+ }
+
+ g_free(irc);
+
+ if (cb)
+ CALLBACK_WITH_FAILURE(cb, data);
+
+ return NULL;
}
-static void isi_answer(struct ofono_voicecall *vc,
- ofono_voicecall_cb_t cb, void *data)
+static void isi_ctx_queue(struct isi_call_req_context *irc,
+ isi_call_req_step *next)
{
+ if (irc->prev == NULL) {
+ struct isi_voicecall *ivc = ofono_voicecall_get_data(irc->ovc);
+
+ irc->prev = &ivc->queue;
+ if ((irc->next = *irc->prev))
+ irc->next->prev = &irc->next;
+ *irc->prev = irc;
+ }
+
+ irc->step = next;
}
-static void isi_hangup(struct ofono_voicecall *vc,
- ofono_voicecall_cb_t cb, void *data)
+static void isi_ctx_remove(struct isi_call_req_context *irc)
{
+ if (irc->prev) {
+ if ((*irc->prev = irc->next)) {
+ irc->next->prev = irc->prev;
+ irc->next = NULL;
+ }
+ irc->prev = NULL;
+ }
}
-static void isi_list_calls(struct ofono_voicecall *vc,
- ofono_call_list_cb_t cb, void *data)
+static void isi_ctx_free(struct isi_call_req_context *irc)
{
+ if (irc) {
+ isi_ctx_remove(irc);
+ g_free(irc);
+ }
}
-static void isi_hold_all_active(struct ofono_voicecall *vc,
- ofono_voicecall_cb_t cb, void *data)
+static bool isi_ctx_return(struct isi_call_req_context *irc,
+ enum ofono_error_type type,
+ int error)
{
+ if (!irc)
+ return true;
+
+ if (irc->cb) {
+ struct ofono_error e = { .type = type, .error = error };
+ irc->cb(&e, irc->data);
+ }
+
+ isi_ctx_free(irc);
+
+ return true;
}
-static void isi_release_all_held(struct ofono_voicecall *vc,
- ofono_voicecall_cb_t cb, void *data)
+static bool isi_ctx_return_failure(struct isi_call_req_context *irc)
+{
+ return isi_ctx_return(irc, OFONO_ERROR_TYPE_FAILURE, 0);
+}
+
+static bool isi_ctx_return_success(struct isi_call_req_context *irc)
+{
+ if (irc && irc->step) {
+ irc->step(irc, 0);
+ return true;
+ }
+
+ return isi_ctx_return(irc, OFONO_ERROR_TYPE_NO_ERROR, 0);
+}
+
+/* ------------------------------------------------------------------------- */
+/* Decoding subblocks */
+
+static void isi_call_any_address_sb_proc(struct isi_voicecall *ivc,
+ struct isi_call *call,
+ GIsiSubBlockIter const *sb)
+{
+ uint8_t addr_type, presentation, addr_len;
+ char *address;
+
+ if (!g_isi_sb_iter_get_byte(sb, &addr_type, 2) ||
+ !g_isi_sb_iter_get_byte(sb, &presentation, 3) ||
+ /* fillerbyte */
+ !g_isi_sb_iter_get_byte(sb, &addr_len, 5) ||
+ !g_isi_sb_iter_get_alpha_tag(sb, &address, 2 * addr_len, 6))
+ return;
+
+ call->addr_type = addr_type | 0x80;
+ call->presentation = presentation;
+ strncpy(call->address, address, sizeof call->address);
+
+ g_free(address);
+}
+
+static void isi_call_origin_address_sb_proc(struct isi_voicecall *ivc,
+ struct isi_call *call,
+ GIsiSubBlockIter const *sb)
+{
+ if (!call->address[0])
+ isi_call_any_address_sb_proc(ivc, call, sb);
+}
+
+static void isi_call_destination_address_sb_proc(struct isi_voicecall *ivc,
+ struct isi_call *call,
+ GIsiSubBlockIter const *sb)
+{
+ if (!call->address[0])
+ isi_call_any_address_sb_proc(ivc, call, sb);
+}
+
+static void isi_call_mode_sb_proc(struct isi_voicecall *ivc,
+ struct isi_call *call,
+ GIsiSubBlockIter const *sb)
+{
+ uint8_t mode, mode_info;
+
+ if (!g_isi_sb_iter_get_byte(sb, &mode, 2) ||
+ !g_isi_sb_iter_get_byte(sb, &mode_info, 3))
+ return;
+
+ call->mode = mode;
+ call->mode_info = mode_info;
+}
+
+static void isi_call_cause_sb_proc(struct isi_voicecall *ivc,
+ struct isi_call *call,
+ GIsiSubBlockIter const *sb)
+{
+ uint8_t cause_type, cause;
+
+ if (!g_isi_sb_iter_get_byte(sb, &cause_type, 2) ||
+ !g_isi_sb_iter_get_byte(sb, &cause, 3))
+ return;
+
+ call->cause_type = cause_type;
+ call->cause = cause;
+}
+
+static void isi_call_status_sb_proc(struct isi_voicecall *ivc,
+ struct isi_call *call,
+ GIsiSubBlockIter const *sb)
+{
+ uint8_t status;
+
+ if (!g_isi_sb_iter_get_byte(sb, &status, 2))
+ return;
+
+ call->status = status;
+}
+
+static struct isi_call *
+isi_call_status_info_sb_proc(struct isi_voicecall *ivc,
+ struct isi_call calls[8],
+ GIsiSubBlockIter const *sb)
{
+ struct isi_call *call = NULL;
+ int i;
+ uint8_t call_id;
+ uint8_t mode;
+ uint8_t mode_info;
+ uint8_t status;
+
+ if (!g_isi_sb_iter_get_byte(sb, &call_id, 2) ||
+ !g_isi_sb_iter_get_byte(sb, &mode, 3) ||
+ !g_isi_sb_iter_get_byte(sb, &mode_info, 4) ||
+ !g_isi_sb_iter_get_byte(sb, &status, 5))
+ return NULL;
+
+ i = call_id & 7;
+
+ if (1 <= i && i <= 7) {
+ call = &calls[i];
+ call->call_id = call_id;
+ call->status = status;
+ call->mode = mode;
+ call->mode_info = mode_info;
+ }
+
+ return call;
}
-static void isi_set_udub(struct ofono_voicecall *vc,
+static struct isi_call *
+isi_call_addr_and_status_info_sb_proc(struct isi_voicecall *ivc,
+ struct isi_call calls[8],
+ GIsiSubBlockIter const *sb)
+{
+ struct isi_call *call = NULL;
+ int i;
+ uint8_t call_id;
+ uint8_t mode;
+ uint8_t mode_info;
+ uint8_t status;
+ uint8_t addr_type;
+ uint8_t presentation;
+ uint8_t addr_len;
+ char *address;
+
+ if (!g_isi_sb_iter_get_byte(sb, &call_id, 2) ||
+ !g_isi_sb_iter_get_byte(sb, &mode, 3) ||
+ !g_isi_sb_iter_get_byte(sb, &mode_info, 4) ||
+ !g_isi_sb_iter_get_byte(sb, &status, 5) ||
+ !g_isi_sb_iter_get_byte(sb, &addr_type, 8) ||
+ !g_isi_sb_iter_get_byte(sb, &presentation, 9) ||
+ !g_isi_sb_iter_get_byte(sb, &addr_len, 11) ||
+ !g_isi_sb_iter_get_alpha_tag(sb, &address, 2 * addr_len, 12))
+ return NULL;
+
+ i = call_id & 7;
+
+ if (1 <= i && i <= 7) {
+ call = &calls[i];
+ call->call_id = call_id;
+ call->status = status;
+ call->mode = mode;
+ call->mode_info = mode_info;
+ call->addr_type = addr_type | 0x80;
+ call->presentation = presentation;
+ strncpy(call->address, address, sizeof call->address);
+ }
+
+ free(address);
+
+ return call;
+}
+
+/* ------------------------------------------------------------------------- */
+/* PN_CALL messages */
+
+static GIsiResponse isi_call_status_resp,
+ isi_call_create_resp,
+ isi_call_answer_resp,
+ isi_call_release_resp,
+ isi_call_control_resp,
+ isi_call_dtmf_send_resp;
+
+static struct isi_call_req_context *
+isi_call_create_req(struct ofono_voicecall *ovc,
+ uint8_t presentation,
+ uint8_t addr_type,
+ char const restrict address[21],
+ ofono_voicecall_cb_t cb,
+ void *data)
+{
+ size_t addr_len = strlen(address);
+ size_t sub_len = (6 + 2 * addr_len + 3) & ~3;
+ size_t i, offset = 3 + 4 + 8 + 6;
+ uint8_t req[3 + 4 + 8 + 6 + 40] = {
+ CALL_CREATE_REQ,
+ 0, /* No id */
+ 3, /* Mode, Clir, Number */
+ /* MODE SB */
+ CALL_MODE, 4, CALL_MODE_SPEECH, CALL_MODE_INFO_NONE,
+ /* ORIGIN_INFO SB */
+ CALL_ORIGIN_INFO, 8, presentation, 0, 0, 0, 0, 0,
+ /* DESTINATION_ADDRESS SB */
+ CALL_DESTINATION_ADDRESS,
+ sub_len,
+ addr_type & 0x7F,
+ 0, 0,
+ addr_len,
+ /* uint16_t addr[20] */
+ };
+ size_t rlen = 3 + 4 + 8 + sub_len;
+
+ if (addr_len > 20) {
+ CALLBACK_WITH_FAILURE(cb, data);
+ return NULL;
+ }
+
+ for (i = 0; i < addr_len; i++)
+ req[offset + 2 * i + 1] = address[i];
+
+ return isi_call_req(ovc, req, rlen, isi_call_create_resp, cb, data);
+}
+
+static bool isi_call_create_resp(GIsiClient *client,
+ void const * restrict data,
+ size_t len,
+ uint16_t object,
+ void *irc)
+{
+ struct {
+ uint8_t message_id, call_id, sub_blocks;
+ } const *m = data;
+
+ if (m != NULL && len < (sizeof *m))
+ return false;
+ if (m == NULL || m->message_id == COMMON_MESSAGE)
+ return isi_ctx_return_failure(irc);
+ if (m->message_id != CALL_CREATE_RESP)
+ return false;
+
+ if (m->call_id != CALL_ID_NONE && m->sub_blocks == 0)
+ return isi_ctx_return_success(irc);
+
+ /* Cause ? */
+ return isi_ctx_return_failure(irc);
+}
+
+static void isi_call_status_ind_cb(GIsiClient *client,
+ void const * restrict data,
+ size_t len,
+ uint16_t object,
+ void *_ovc)
+{
+ struct ofono_voicecall *ovc = _ovc;
+ struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
+ struct {
+ uint8_t message_id, call_id, sub_blocks;
+ } const *m = data;
+ struct isi_call *call;
+ uint8_t old;
+ GIsiSubBlockIter sb[1];
+
+ if (len < 3)
+ return; /* runt */
+
+ if ((m->call_id & 7) == 0)
+ return;
+
+ call = &ivc->calls[m->call_id & 7];
+
+ old = call->status;
+ call->call_id = m->call_id;
+
+ for (g_isi_sb_iter_init(sb, data, len, (sizeof *m));
+ g_isi_sb_iter_is_valid(sb);
+ g_isi_sb_iter_next(sb)) {
+ switch (g_isi_sb_iter_get_id(sb)) {
+ case CALL_STATUS:
+ isi_call_status_sb_proc(ivc, call, sb);
+ break;
+
+ case CALL_MODE:
+ isi_call_mode_sb_proc(ivc, call, sb);
+ break;
+
+ case CALL_CAUSE:
+ isi_call_cause_sb_proc(ivc, call, sb);
+ break;
+
+ case CALL_DESTINATION_ADDRESS:
+ isi_call_destination_address_sb_proc(ivc, call, sb);
+ break;
+
+ case CALL_ORIGIN_ADDRESS:
+ isi_call_origin_address_sb_proc(ivc, call, sb);
+ break;
+
+ case CALL_GSM_DETAILED_CAUSE:
+ case CALL_DESTINATION_PRE_ADDRESS:
+ case CALL_DESTINATION_POST_ADDRESS:
+ case CALL_DESTINATION_SUBADDRESS:
+ case CALL_GSM_EVENT_INFO:
+ case CALL_NW_CAUSE:
+ break;
+ }
+ }
+
+ if (old != call->status) {
+ if (call->status == CALL_STATUS_IDLE) {
+ call->status = CALL_STATUS_TERMINATED;
+ isi_call_notify(ovc, call);
+ isi_call_set_idle(call);
+ return;
+ }
+ }
+
+ isi_call_notify(ovc, call);
+}
+
+static struct isi_call_req_context *
+isi_call_answer_req(struct ofono_voicecall *ovc,
+ uint8_t call_id,
+ ofono_voicecall_cb_t cb,
+ void *data)
+{
+ uint8_t const req[] = {
+ CALL_ANSWER_REQ, call_id, 0
+ };
+ size_t rlen = sizeof req;
+
+ return isi_call_req(ovc, req, rlen, isi_call_answer_resp, cb, data);
+}
+
+static bool isi_call_answer_resp(GIsiClient *client,
+ void const * restrict data,
+ size_t len,
+ uint16_t object,
+ void *irc)
+{
+ struct {
+ uint8_t message_id, call_id, sub_blocks;
+ } const *m = data;
+
+ if (m != NULL && len < (sizeof *m))
+ return false;
+ if (m == NULL || m->message_id == COMMON_MESSAGE)
+ return isi_ctx_return_failure(irc);
+ if (m->message_id != CALL_ANSWER_RESP)
+ return false;
+
+ if (m->call_id != CALL_ID_NONE && m->sub_blocks == 0)
+ return isi_ctx_return_success(irc);
+
+ /* Cause ? */
+ return isi_ctx_return_failure(irc);
+}
+
+static struct isi_call_req_context *
+isi_call_release_req(struct ofono_voicecall *ovc,
+ uint8_t call_id,
+ enum isi_call_cause_type cause_type,
+ uint8_t cause,
+ ofono_voicecall_cb_t cb,
+ void *data)
+{
+ uint8_t const req[] = {
+ CALL_RELEASE_REQ, call_id, 1,
+ CALL_CAUSE, 4, cause_type, cause,
+ };
+ size_t rlen = sizeof req;
+
+ return isi_call_req(ovc, req, rlen, isi_call_release_resp, cb, data);
+}
+
+static bool isi_call_release_resp(GIsiClient *client,
+ void const * restrict data,
+ size_t len,
+ uint16_t object,
+ void *irc)
+{
+ struct {
+ uint8_t message_id, call_id, sub_blocks;
+ } const *m = data;
+ GIsiSubBlockIter i[1];
+ uint8_t cause_type = 0, cause = 0;
+
+ if (m != NULL && len < (sizeof *m))
+ return false;
+ if (m == NULL || m->message_id == COMMON_MESSAGE)
+ return isi_ctx_return_failure(irc);
+ if (m->message_id != CALL_RELEASE_RESP)
+ return false;
+
+ for (g_isi_sb_iter_init(i, m, len, (sizeof *m));
+ g_isi_sb_iter_is_valid(i);
+ g_isi_sb_iter_next(i)) {
+ if (g_isi_sb_iter_get_id(i) != CALL_CAUSE ||
+ !g_isi_sb_iter_get_byte(i, &cause_type, 2) ||
+ !g_isi_sb_iter_get_byte(i, &cause, 3))
+ continue;
+ }
+
+ if ((cause_type == CALL_CAUSE_TYPE_SERVER ||
+ cause_type == CALL_CAUSE_TYPE_CLIENT) &&
+ (cause == CALL_CAUSE_RELEASE_BY_USER ||
+ cause == CALL_CAUSE_BUSY_USER_REQUEST))
+ return isi_ctx_return_success(irc);
+ else
+ return isi_ctx_return_failure(irc);
+}
+
+static struct isi_call_req_context *
+isi_call_status_req(struct ofono_voicecall *ovc,
+ uint8_t id,
+ uint8_t mode,
+ ofono_voicecall_cb_t cb,
+ void *data)
+{
+ unsigned char req[] = {
+ CALL_STATUS_REQ, id, 1,
+ CALL_STATUS_MODE, 4, mode, 0,
+ };
+ size_t rlen = sizeof req;
+
+ return isi_call_req(ovc, req, rlen, isi_call_status_resp, cb, data);
+}
+
+
+static bool isi_call_status_resp(GIsiClient *client,
+ void const * restrict data,
+ size_t len,
+ uint16_t object,
+ void *_irc)
+{
+ struct isi_call_req_context *irc = _irc;
+ struct ofono_voicecall *ovc = irc->ovc;
+ struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
+ struct {
+ uint8_t message_id, call_id, sub_blocks;
+ } const *m = data;
+ GIsiSubBlockIter sb[1];
+ struct isi_call *call = NULL;
+
+ if (m != NULL && len < (sizeof *m))
+ return false;
+ if (m == NULL || m->message_id == COMMON_MESSAGE)
+ return isi_ctx_return_failure(irc);
+ if (m->message_id != CALL_STATUS_RESP)
+ return false;
+
+ for (g_isi_sb_iter_init(sb, m, len, (sizeof *m));
+ g_isi_sb_iter_is_valid(sb);
+ g_isi_sb_iter_next(sb)) {
+ switch (g_isi_sb_iter_get_id(sb)) {
+
+ case CALL_STATUS_INFO:
+ call = isi_call_status_info_sb_proc(ivc, ivc->calls, sb);
+ break;
+
+ case CALL_ADDR_AND_STATUS_INFO:
+ call = isi_call_addr_and_status_info_sb_proc(ivc, ivc->calls, sb);
+ if (call)
+ isi_call_notify(ovc, call);
+ break;
+
+ case CALL_CAUSE:
+ if (call)
+ isi_call_cause_sb_proc(ivc, call, sb);
+ break;
+ }
+ }
+
+ return isi_ctx_return_success(irc);
+}
+
+static struct isi_call_req_context *
+isi_call_control_req(struct ofono_voicecall *ovc,
+ uint8_t call_id,
+ enum isi_call_operation op,
+ uint8_t info,
+ ofono_voicecall_cb_t cb,
+ void *data)
+{
+ uint8_t const req[] = {
+ CALL_CONTROL_REQ, call_id, 1,
+ CALL_OPERATION, 4, op, info,
+ };
+ size_t rlen = sizeof req;
+
+ return isi_call_req(ovc, req, rlen, isi_call_control_resp, cb, data);
+}
+
+static struct isi_call_req_context *
+isi_call_deflect_req(struct ofono_voicecall *ovc,
+ uint8_t call_id,
+ uint8_t address_type,
+ char const restrict address[21],
+ ofono_voicecall_cb_t cb,
+ void *data)
+{
+ size_t addr_len = strlen(address);
+ size_t sub_len = (6 + 2 * addr_len + 3) & ~3;
+ size_t i, offset = 3 + 4 + 6;
+ size_t rlen = 3 + 4 + sub_len;
+ uint8_t req[3 + 4 + 6 + 40] = {
+ CALL_CONTROL_REQ, call_id, 2,
+ CALL_OPERATION, 4, CALL_GSM_OP_DEFLECT, 0,
+ CALL_GSM_DEFLECTION_ADDRESS, sub_len,
+ address_type & 0x7F,
+ 0x7, /* default presentation */
+ 0, /* filler */
+ addr_len,
+ };
+
+ if (addr_len > 20) {
+ CALLBACK_WITH_FAILURE(cb, data);
+ return false;
+ }
+
+ for (i = 0; i < addr_len; i++)
+ req[offset + 2 * i + 1] = address[i];
+
+ return isi_call_req(ovc, req, rlen, isi_call_control_resp, cb, data);
+}
+
+static bool isi_call_control_resp(GIsiClient *client,
+ void const * restrict data,
+ size_t len,
+ uint16_t object,
+ void *irc)
+{
+ struct {
+ uint8_t message_id, call_id, sub_blocks;
+ } const *m = data;
+ GIsiSubBlockIter i[1];
+ uint8_t cause_type = 0, cause = 0;
+
+ if (m != NULL && len < sizeof *m)
+ return false;
+ if (m == NULL || m->message_id == COMMON_MESSAGE)
+ return isi_ctx_return_failure(irc);
+ if (m->message_id != CALL_CONTROL_RESP)
+ return false;
+
+ for (g_isi_sb_iter_init(i, m, len, (sizeof *m));
+ g_isi_sb_iter_is_valid(i);
+ g_isi_sb_iter_next(i)) {
+ if (g_isi_sb_iter_get_id(i) != CALL_CAUSE ||
+ !g_isi_sb_iter_get_byte(i, &cause_type, 2) ||
+ !g_isi_sb_iter_get_byte(i, &cause, 3))
+ continue;
+ }
+
+ if (!cause)
+ return isi_ctx_return_success(irc);
+ else
+ return isi_ctx_return_failure(irc);
+}
+
+static struct isi_call_req_context *
+isi_call_dtmf_send_req(struct ofono_voicecall *ovc,
+ uint8_t call_id,
+ char const *string,
+ ofono_voicecall_cb_t cb,
+ void *data)
+{
+ size_t str_len = strlen(string);
+ size_t sub_len = 4 + ((2 * str_len + 3) & ~3);
+ size_t i, offset = 3 + 4 + 8 + 4;
+ size_t rlen = 3 + 4 + 8 + sub_len;
+ uint8_t req[3 + 4 + 8 + (255 & ~3)] = {
+ CALL_DTMF_SEND_REQ, call_id, 3,
+ CALL_DTMF_INFO, 4, CALL_DTMF_ENABLE_TONE_IND_SEND, 0,
+ CALL_DTMF_TIMERS, 8,
+ 0, 200, /* duration in ms */
+ 0, 100, /* gap in ms */
+ 0, 0, /* filler */
+ CALL_DTMF_STRING, sub_len,
+ 100, /* pause length */
+ str_len,
+ /* string */
+ };
+
+ if (sub_len >= 256) {
+ CALLBACK_WITH_FAILURE(cb, data);
+ return false;
+ }
+
+ for (i = 0; i < str_len; i++)
+ req[offset + 2 * i + 1] = string[i];
+
+ return isi_call_req(ovc, req, rlen, isi_call_dtmf_send_resp, cb, data);
+}
+
+static bool isi_call_dtmf_send_resp(GIsiClient *client,
+ void const * restrict data,
+ size_t len,
+ uint16_t object,
+ void *irc)
+{
+ struct {
+ uint8_t message_id, call_id, sub_blocks;
+ } const *m = data;
+ GIsiSubBlockIter i[1];
+ uint8_t cause_type = 0, cause = 0;
+
+ if (m != NULL && len < (sizeof *m))
+ return false;
+ if (m == NULL || m->message_id == COMMON_MESSAGE)
+ return isi_ctx_return_failure(irc);
+ if (m->message_id != CALL_DTMF_SEND_RESP)
+ return false;
+
+ if (m->sub_blocks == 0)
+ return isi_ctx_return_success(irc);
+
+ for (g_isi_sb_iter_init(i, m, len, (sizeof *m));
+ g_isi_sb_iter_is_valid(i);
+ g_isi_sb_iter_next(i)) {
+ if (g_isi_sb_iter_get_id(i) != CALL_CAUSE ||
+ !g_isi_sb_iter_get_byte(i, &cause_type, 2) ||
+ !g_isi_sb_iter_get_byte(i, &cause, 3))
+ continue;
+ }
+
+ if (!cause)
+ return isi_ctx_return_success(irc);
+ else
+ return isi_ctx_return_failure(irc);
+}
+
+
+/* ------------------------------------------------------------------------- */
+/* Notify */
+
+static void isi_call_notify(struct ofono_voicecall *ovc,
+ struct isi_call *call)
+{
+ struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
+ struct isi_call_req_context *irc, **queue;
+ struct ofono_call ocall;
+
+ DBG("called with status=%s (0x%02X)",
+ isi_call_status_name(call->status), call->status);
+
+ for (queue = &ivc->queue; (irc = *queue);) {
+ irc->step(irc, call->status);
+ if (*queue == irc)
+ queue = &irc->next;
+ }
+
+ switch (call->status) {
+ case CALL_STATUS_IDLE:
+ case CALL_STATUS_MO_RELEASE:
+ case CALL_STATUS_MT_RELEASE:
+ case CALL_STATUS_TERMINATED:
+ isi_call_release(ovc, call);
+ return;
+ }
+
+ ocall = isi_call_as_ofono_call(call);
+
+ DBG("id=%u,%s,%u,\"%s\",%u,%u",
+ ocall.id,
+ ocall.direction ? "terminated" : "originated",
+ ocall.status,
+ ocall.phone_number.number,
+ ocall.phone_number.type,
+ ocall.clip_validity);
+
+ ofono_voicecall_notify(ovc, &ocall);
+}
+
+static void isi_call_release(struct ofono_voicecall *ovc,
+ struct isi_call *call)
+{
+ struct ofono_error error = { OFONO_ERROR_TYPE_NO_ERROR, 0 };
+ enum ofono_disconnect_reason reason;
+
+ switch (call->status) {
+ case CALL_STATUS_IDLE:
+ reason = OFONO_DISCONNECT_REASON_UNKNOWN;
+ break;
+ case CALL_STATUS_MO_RELEASE:
+ reason = OFONO_DISCONNECT_REASON_LOCAL_HANGUP;
+ break;
+ case CALL_STATUS_MT_RELEASE:
+ reason = OFONO_DISCONNECT_REASON_REMOTE_HANGUP;
+ break;
+ case CALL_STATUS_TERMINATED:
+ default:
+ reason = OFONO_DISCONNECT_REASON_ERROR;
+ break;
+ }
+
+ if (!call->reason) {
+ call->reason = reason;
+ DBG("disconnected id=%u reason=%u", call->id, reason);
+ ofono_voicecall_disconnected(ovc, call->id, reason, &error);
+ }
+
+ if (!reason)
+ isi_call_set_idle(call);
+}
+
+static struct ofono_call
+isi_call_as_ofono_call(struct isi_call const *call)
+{
+ struct ofono_call ocall = { call->id };
+ struct ofono_phone_number *number = &ocall.phone_number;
+
+ ocall.type = 0; /* Voice call */
+ ocall.direction = call->mode_info & CALL_MODE_ORIGINATOR;
+ ocall.status = isi_call_status_to_clcc(call);
+ memcpy(number->number, call->address, sizeof number->number);
+ number->type = 0x80 | call->addr_type;
+ ocall.clip_validity = call->presentation & 3;
+ if (ocall.clip_validity == 0 && strlen(number->number) == 0)
+ ocall.clip_validity = 2;
+
+ return ocall;
+}
+
+/** Get +CLCC status */
+static int
+isi_call_status_to_clcc(struct isi_call const *call)
+{
+ switch (call->status) {
+ case CALL_STATUS_CREATE:
+ return 2;
+ case CALL_STATUS_COMING:
+ return 4;
+ case CALL_STATUS_PROCEEDING:
+ if ((call->mode_info & CALL_MODE_ORIGINATOR))
+ return 4;
+ else
+ return 2;
+ case CALL_STATUS_MO_ALERTING:
+ return 3;
+ case CALL_STATUS_MT_ALERTING:
+ return 4;
+ case CALL_STATUS_WAITING:
+ return 5;
+
+ case CALL_STATUS_ANSWERED:
+ case CALL_STATUS_ACTIVE:
+ case CALL_STATUS_MO_RELEASE:
+ case CALL_STATUS_MT_RELEASE:
+ case CALL_STATUS_HOLD_INITIATED:
+ return 0;
+
+ case CALL_STATUS_HOLD:
+ case CALL_STATUS_RETRIEVE_INITIATED:
+ return 1;
+
+ case CALL_STATUS_RECONNECT_PENDING:
+ case CALL_STATUS_TERMINATED:
+ case CALL_STATUS_SWAP_INITIATED:
+ return 0;
+ }
+
+ return 0;
+}
+
+static struct isi_call *isi_call_set_idle(struct isi_call *call)
+{
+ uint8_t id;
+
+ if (call) {
+ id = call->id;
+ memset(call, 0, sizeof *call);
+ call->id = id;
+ }
+
+ return call;
+}
+
+/* ---------------------------------------------------------------------- */
+
+static void isi_dial(struct ofono_voicecall *ovc,
+ const struct ofono_phone_number * restrict number,
+ enum ofono_clir_option clir,
+ enum ofono_cug_option cug,
+ ofono_voicecall_cb_t cb,
+ void *data)
+{
+ unsigned char presentation = CALL_GSM_PRESENTATION_DEFAULT;
+
+ switch (clir)
+ {
+ case OFONO_CLIR_OPTION_DEFAULT:
+ presentation = CALL_GSM_PRESENTATION_DEFAULT;
+ break;
+ case OFONO_CLIR_OPTION_INVOCATION:
+ presentation = CALL_PRESENTATION_RESTRICTED;
+ break;
+ case OFONO_CLIR_OPTION_SUPPRESSION:
+ presentation = CALL_PRESENTATION_ALLOWED;
+ break;
+ }
+
+ switch (cug)
+ {
+ case OFONO_CUG_OPTION_DEFAULT:
+ break;
+ case OFONO_CUG_OPTION_INVOCATION:
+ /* Not implemented */
+ CALLBACK_WITH_FAILURE(cb, data);
+ return;
+ }
+
+ isi_call_create_req(ovc, presentation,
+ number->type,
+ number->number,
+ cb, data);
+}
+
+static void isi_answer(struct ofono_voicecall *ovc,
+ ofono_voicecall_cb_t cb, void *data)
+{
+ isi_call_answer_req(ovc, CALL_ID_ALL, cb, data);
+}
+
+static void isi_hangup(struct ofono_voicecall *ovc,
ofono_voicecall_cb_t cb, void *data)
{
+ /* AT+CHUP */
+ isi_call_release_req(ovc, CALL_ID_ALL,
+ CALL_CAUSE_TYPE_CLIENT,
+ CALL_CAUSE_RELEASE_BY_USER,
+ cb, data);
+}
+
+static void isi_list_calls(struct ofono_voicecall *ovc,
+ ofono_call_list_cb_t cb, void *data)
+{
+ struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
+ struct ofono_call calls[7];
+ int n, i;
+
+ for (i = 1, n = 0; i <= 7; i++) {
+ if (ivc->calls[i].status != CALL_STATUS_IDLE)
+ calls[n++] = isi_call_as_ofono_call(&ivc->calls[i]);
+ }
+
+ CALLBACK_WITH_FAILURE(cb, n, calls, data);
}
-static void isi_release_all_active(struct ofono_voicecall *vc,
+static void isi_release_all_held(struct ofono_voicecall *ovc,
ofono_voicecall_cb_t cb, void *data)
{
+ /* AT+CHLD=0 (w/out incoming calls) */
+ isi_call_release_req(ovc, CALL_ID_HOLD,
+ CALL_CAUSE_TYPE_CLIENT,
+ CALL_CAUSE_RELEASE_BY_USER,
+ cb, data);
}
-static void isi_release_specific(struct ofono_voicecall *vc, int id,
+static void isi_set_udub(struct ofono_voicecall *ovc,
ofono_voicecall_cb_t cb, void *data)
{
+ /* AT+CHLD=0 (w/ incoming calls) */
+ struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
+ int id = 0;
+
+ for (id = 1; id <= 7; id++) {
+ if (ivc->calls[id].status == CALL_STATUS_WAITING)
+ break;
+ if (ivc->calls[id].status == CALL_STATUS_MT_ALERTING)
+ break;
+ if (ivc->calls[id].status == CALL_STATUS_COMING)
+ break;
+ }
+
+ if (id <= 7)
+ isi_call_release_req(ovc, id,
+ CALL_CAUSE_TYPE_CLIENT,
+ CALL_CAUSE_BUSY_USER_REQUEST,
+ cb, data);
+ else
+ CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void isi_retrieve(struct ofono_voicecall *ovc,
+ ofono_voicecall_cb_t cb, void *data)
+{
+ isi_call_control_req(ovc, CALL_ID_HOLD, CALL_OP_RETRIEVE, 0,
+ cb, data);
+}
+
+static isi_call_req_step isi_wait_and_answer, isi_wait_and_retrieve;
+
+static void isi_release_all_active(struct ofono_voicecall *ovc,
+ ofono_voicecall_cb_t cb, void *data)
+{
+ /* AT+CHLD=1 */
+ struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
+ int id = 0, waiting = 0, active = 0, hold = 0;
+
+ for (id = 1; id <= 7; id++) {
+ if (ivc->calls[id].call_id & CALL_ID_WAITING)
+ waiting++;
+ if (ivc->calls[id].call_id & CALL_ID_HOLD)
+ hold++;
+ if (ivc->calls[id].call_id & CALL_ID_ACTIVE)
+ active++;
+ }
+
+ if (active) {
+ struct isi_call_req_context *irc;
+
+ irc = isi_call_release_req(ovc, CALL_ID_ACTIVE,
+ CALL_CAUSE_TYPE_CLIENT,
+ CALL_CAUSE_RELEASE_BY_USER,
+ cb, data);
+
+ if (irc == NULL)
+ ;
+ else if (waiting)
+ isi_ctx_queue(irc, isi_wait_and_answer);
+ else if (hold)
+ isi_ctx_queue(irc, isi_wait_and_retrieve);
+ }
+ else
+ CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void isi_wait_and_answer(struct isi_call_req_context *irc,
+ int event)
+{
+ DBG("irc=%p event=%u", (void *)irc, event);
+ switch (event) {
+ case CALL_STATUS_TERMINATED:
+ isi_answer(irc->ovc, irc->cb, irc->data);
+ isi_ctx_free(irc);
+ break;
+ }
}
-static void isi_private_chat(struct ofono_voicecall *vc, int id,
+static void isi_wait_and_retrieve(struct isi_call_req_context *irc,
+ int event)
+{
+ DBG("irc=%p event=%u", (void *)irc, event);
+ switch (event) {
+ case CALL_STATUS_TERMINATED:
+ isi_retrieve(irc->ovc, irc->cb, irc->data);
+ isi_ctx_free(irc);
+ break;
+ }
+}
+
+static void isi_hold_all_active(struct ofono_voicecall *ovc,
ofono_voicecall_cb_t cb, void *data)
{
+ /* AT+CHLD=2 */
+ struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
+ int id = 0, op = 0, waiting = 0, active = 0, hold = 0;
+
+ for (id = 1; id <= 7; id++) {
+ if (ivc->calls[id].call_id & CALL_ID_WAITING)
+ waiting++;
+ if (ivc->calls[id].call_id & CALL_ID_HOLD)
+ hold++;
+ if (ivc->calls[id].call_id & CALL_ID_ACTIVE)
+ active++;
+ }
+
+ if (waiting) {
+ isi_call_answer_req(ovc, CALL_ID_WAITING, cb, data);
+ }
+ else if (hold) {
+ if (active) {
+ id = CALL_ID_ACTIVE, op = CALL_OP_SWAP;
+ }
+ else {
+ id = CALL_ID_HOLD, op = CALL_OP_RETRIEVE;
+ }
+ isi_call_control_req(ovc, id, op, 0, cb, data);
+ }
+ else if (active) {
+ id = CALL_ID_ACTIVE, op = CALL_OP_HOLD;
+ isi_call_control_req(ovc, id, op, 0, cb, data);
+ }
+ else {
+ CALLBACK_WITH_FAILURE(cb, data);
+ }
}
-static void isi_create_multiparty(struct ofono_voicecall *vc,
+static void isi_release_specific(struct ofono_voicecall *ovc, int id,
ofono_voicecall_cb_t cb, void *data)
{
+ /* AT+CHLD=1X */
+ struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
+
+ if (1 <= id && id <= 7) {
+ struct isi_call const *status = &ivc->calls[id];
+ uint8_t cause = CALL_CAUSE_RELEASE_BY_USER;
+
+ switch (status->status) {
+ case CALL_STATUS_MT_ALERTING:
+ case CALL_STATUS_WAITING:
+ cause = CALL_CAUSE_BUSY_USER_REQUEST;
+ break;
+ case CALL_STATUS_PROCEEDING:
+ if ((status->mode_info & CALL_MODE_ORIGINATOR))
+ cause = CALL_CAUSE_BUSY_USER_REQUEST;
+ break;
+ }
+
+ isi_call_release_req(ovc, id,
+ CALL_CAUSE_TYPE_CLIENT, cause,
+ cb, data);
+ }
+ else
+ CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void isi_private_chat(struct ofono_voicecall *ovc, int id,
+ ofono_voicecall_cb_t cb, void *data)
+{
+ /* AT+CHLD=2X */
+ if (1 <= id && id <= 7)
+ isi_call_control_req(ovc,
+ id, CALL_OP_CONFERENCE_SPLIT, 0,
+ cb, data);
+ else
+ CALLBACK_WITH_FAILURE(cb, data);
+}
+
+static void isi_create_multiparty(struct ofono_voicecall *ovc,
+ ofono_voicecall_cb_t cb, void *data)
+{
+ /* AT+CHLD=3 */
+ isi_call_control_req(ovc,
+ CALL_ID_ALL, CALL_OP_CONFERENCE_BUILD, 0,
+ cb, data);
}
-static void isi_transfer(struct ofono_voicecall *vc,
+static void isi_transfer(struct ofono_voicecall *ovc,
ofono_voicecall_cb_t cb, void *data)
{
+ /* AT+CHLD=4 */
+ struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
+
+ uint8_t id;
+
+ for (id = 1; id <= 7; id++)
+ if (ivc->calls[id].status == CALL_STATUS_MO_ALERTING)
+ break;
+ if (id > 7)
+ id = CALL_ID_ACTIVE;
+
+ isi_call_control_req(ovc,
+ id, CALL_GSM_OP_TRANSFER, 0,
+ cb, data);
}
-static void isi_deflect(struct ofono_voicecall *vc,
+static void isi_deflect(struct ofono_voicecall *ovc,
const struct ofono_phone_number *ph,
ofono_voicecall_cb_t cb, void *data)
{
+ /* AT+CTFR=<number>,<type> */
+ int id = CALL_ID_WAITING;
+ isi_call_deflect_req(ovc, id, ph->type, ph->number,
+ cb, data);
}
-static void isi_swap_without_accept(struct ofono_voicecall *vc,
+static void isi_swap_without_accept(struct ofono_voicecall *ovc,
ofono_voicecall_cb_t cb, void *data)
{
+ struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
+ int id = 0, op = 0, active = 0, hold = 0;
+
+ for (id = 1; id <= 7; id++) {
+ if (ivc->calls[id].call_id & CALL_ID_HOLD)
+ hold++;
+ if (ivc->calls[id].call_id & CALL_ID_ACTIVE)
+ active++;
+ }
+
+ if (hold && active) {
+ id = CALL_ID_ACTIVE, op = CALL_OP_SWAP;
+ }
+ else if (active) {
+ id = CALL_ID_ACTIVE, op = CALL_OP_HOLD;
+ }
+ else if (hold) {
+ id = CALL_ID_HOLD, op = CALL_OP_RETRIEVE;
+ }
+ else {
+ CALLBACK_WITH_FAILURE(cb, data);
+ return;
+ }
+
+ isi_call_control_req(ovc, id, op, 0, cb, data);
}
-static void isi_send_tones(struct ofono_voicecall *vc, const char *tones,
- ofono_voicecall_cb_t cb, void *data)
+static void isi_send_tones(struct ofono_voicecall *ovc, const char *tones,
+ ofono_voicecall_cb_t cb, void *data)
{
+ isi_call_dtmf_send_req(ovc, CALL_ID_ALL, tones, cb, data);;
}
-static int isi_voicecall_probe(struct ofono_voicecall *call,
- unsigned int vendor, void *user)
+static int isi_voicecall_probe(struct ofono_voicecall *ovc,
+ unsigned int vendor,
+ void *user)
{
GIsiModem *idx = user;
- struct voicecall_data *data = g_try_new0(struct voicecall_data, 1);
+ struct isi_voicecall *ivc = g_try_new0(struct isi_voicecall, 1);
+ int id;
- if (!data)
+ if (!ivc)
return -ENOMEM;
- data->client = g_isi_client_create(idx, PN_CALL);
- if (!data->client)
+ for (id = 0; id <= 7; id++)
+ ivc->calls[id].id = id;
+
+ ivc->client = g_isi_client_create(idx, PN_CALL);
+ if (!ivc->client) {
+ g_free(ivc);
return -ENOMEM;
+ }
- ofono_voicecall_set_data(call, data);
+ ofono_voicecall_set_data(ovc, ivc);
+
+ if (!g_isi_verify(ivc->client, isi_call_verify_cb, ovc))
+ DBG("Unable to verify reachability");
return 0;
}
+static void isi_call_verify_cb(GIsiClient *client,
+ bool alive,
+ void *ovc)
+{
+ if (alive) {
+ DBG("PN_CALL (0x%02X) with version %03d.%03d reachable",
+ g_isi_client_resource(client),
+ g_isi_version_major(client),
+ g_isi_version_minor(client));
+ g_idle_add(isi_call_register, ovc);
+ }
+ else {
+ DBG("Unable to bootstrap voice call driver");
+ }
+}
+
+static gboolean isi_call_register(gpointer _ovc)
+{
+ struct ofono_voicecall *ovc = _ovc;
+ struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
+
+ g_isi_client_set_debug(ivc->client, isi_call_debug, NULL);
+
+ g_isi_subscribe(ivc->client,
+ CALL_STATUS_IND, isi_call_status_ind_cb,
+ ovc);
+
+ if (!isi_call_status_req(ovc,
+ CALL_ID_ALL,
+ CALL_STATUS_MODE_ADDR_AND_ORIGIN,
+ NULL, NULL))
+ DBG("Failed to request call status");
+
+ ofono_voicecall_register(ovc);
+
+ return FALSE;
+}
+
static void isi_voicecall_remove(struct ofono_voicecall *call)
{
- struct voicecall_data *data = ofono_voicecall_get_data(call);
+ struct isi_voicecall *data = ofono_voicecall_get_data(call);
if (data) {
g_isi_client_destroy(data->client);
diff --git a/gisi/client.c b/gisi/client.c
index c592c3c9..f0db24ea 100644
--- a/gisi/client.c
+++ b/gisi/client.c
@@ -431,16 +431,15 @@ static gboolean g_isi_callback(GIOChannel *channel, GIOCondition cond,
msg = (uint8_t *)buf;
+ if (cl->debug_func)
+ cl->debug_func(msg + 1, len - 1, cl->debug_data);
+
if (indication) {
/* Message ID at offset 1 */
id = msg[1];
if (cl->ind.func[id] == NULL)
return TRUE; /* Unsubscribed indication */
- if (cl->debug_func)
- cl->debug_func(msg + 1, len - 1,
- cl->debug_data);
-
cl->ind.func[id](cl, msg + 1, len - 1, obj,
cl->ind.data[id]);
} else {
@@ -449,10 +448,6 @@ static gboolean g_isi_callback(GIOChannel *channel, GIOCondition cond,
if (cl->func[id] == NULL)
return TRUE; /* Bad transaction ID */
- if (cl->debug_func)
- cl->debug_func(msg + 1, len - 1,
- cl->debug_data);
-
if ((cl->func[id])(cl, msg + 1, len - 1, obj,
cl->data[id]))
g_isi_request_cancel(g_isi_req(cl, id));