Add calypso voicecalls driver
authorDenis Kenzior <denis.kenzior@intel.com>
Sat, 5 Sep 2009 01:17:29 +0000 (20:17 -0500)
committerDenis Kenzior <denkenz@gmail.com>
Sat, 5 Sep 2009 01:37:50 +0000 (20:37 -0500)
Makefile.am
drivers/calypsomodem/calypsomodem.c [new file with mode: 0644]
drivers/calypsomodem/calypsomodem.h [new file with mode: 0644]
drivers/calypsomodem/voicecall.c [new file with mode: 0644]

index 816c51f..1d63842 100644 (file)
@@ -100,6 +100,12 @@ builtin_sources += $(gatchat_sources) drivers/atmodem/atmodem.h \
                                drivers/atmodem/atutil.h \
                                drivers/atmodem/atutil.c
 
+builtin_modules += calypsomodem
+builtin_sources += drivers/atmodem/atutil.h \
+                       drivers/calypsomodem/calypsomodem.h \
+                       drivers/calypsomodem/calypsomodem.c \
+                       drivers/calypsomodem/voicecall.c
+
 builtin_modules += modemconf
 builtin_sources += plugins/modemconf.c
 
diff --git a/drivers/calypsomodem/calypsomodem.c b/drivers/calypsomodem/calypsomodem.c
new file mode 100644 (file)
index 0000000..0cc70b6
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2009  Intel Corporation. All rights reserved.
+ *
+ *  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
+
+#include <glib.h>
+#include <gatchat.h>
+
+#define OFONO_API_SUBJECT_TO_CHANGE
+#include <ofono/plugin.h>
+#include <ofono/types.h>
+
+#include "calypsomodem.h"
+
+static int calypsomodem_init(void)
+{
+       calypso_voicecall_init();
+
+       return 0;
+}
+
+static void calypsomodem_exit(void)
+{
+       calypso_voicecall_exit();
+}
+
+OFONO_PLUGIN_DEFINE(calypsomodem, "Calypso modem driver", VERSION,
+                       OFONO_PLUGIN_PRIORITY_DEFAULT,
+                       calypsomodem_init, calypsomodem_exit)
diff --git a/drivers/calypsomodem/calypsomodem.h b/drivers/calypsomodem/calypsomodem.h
new file mode 100644 (file)
index 0000000..cb3d16a
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2009  Intel Corporation. All rights reserved.
+ *
+ *  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
+ *
+ */
+
+#include <drivers/atmodem/atutil.h>
+
+extern void calypso_voicecall_init();
+extern void calypso_voicecall_exit();
diff --git a/drivers/calypsomodem/voicecall.c b/drivers/calypsomodem/voicecall.c
new file mode 100644 (file)
index 0000000..925ce79
--- /dev/null
@@ -0,0 +1,442 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2009  Intel Corporation. All rights reserved.
+ *
+ *  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 <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/voicecall.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "calypsomodem.h"
+
+static const char *none_prefix[] = { NULL };
+
+struct voicecall_data {
+       GAtChat *chat;
+};
+
+static void calypso_generic_cb(gboolean ok, GAtResult *result,
+                               gpointer user_data)
+{
+       struct cb_data *cbd = user_data;
+       ofono_voicecall_cb_t cb = cbd->cb;
+       struct ofono_error error;
+
+       dump_response("calypso_generic_cb", ok, result);
+       decode_at_error(&error, g_at_result_final_response(result));
+
+       cb(&error, cbd->data);
+}
+
+static void calypso_template(struct ofono_voicecall *vc, const char *cmd,
+                               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);
+
+       if (!cbd)
+               goto error;
+
+       if (g_at_chat_send(vd->chat, cmd, none_prefix,
+                               calypso_generic_cb, cbd, g_free) > 0)
+               return;
+
+error:
+       if (cbd)
+               g_free(cbd);
+
+       {
+               DECLARE_FAILURE(error);
+               cb(&error, data);
+       }
+}
+
+static void calypso_dial(struct ofono_voicecall *vc,
+                               const struct ofono_phone_number *ph,
+                               enum ofono_clir_option clir,
+                               enum ofono_cug_option cug,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       char buf[256];
+
+       if (ph->type == 145)
+               sprintf(buf, "ATD+%s", ph->number);
+       else
+               sprintf(buf, "ATD%s", ph->number);
+
+       switch (clir) {
+       case OFONO_CLIR_OPTION_INVOCATION:
+               strcat(buf, "I");
+               break;
+       case OFONO_CLIR_OPTION_SUPPRESSION:
+               strcat(buf, "i");
+               break;
+       default:
+               break;
+       }
+
+       switch (cug) {
+       case OFONO_CUG_OPTION_INVOCATION:
+               strcat(buf, "G");
+               break;
+       default:
+               break;
+       }
+
+       strcat(buf, ";");
+
+       calypso_template(vc, buf, cb, data);
+}
+
+static void calypso_answer(struct ofono_voicecall *vc,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       calypso_template(vc, "ATA", cb, data);
+}
+
+static void calypso_hangup(struct ofono_voicecall *vc,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       calypso_template(vc, "ATH", cb, data);
+}
+
+static void calypso_hold_all_active(struct ofono_voicecall *vc,
+                                       ofono_voicecall_cb_t cb, void *data)
+{
+       calypso_template(vc, "AT+CHLD=2", cb, data);
+}
+
+static void calypso_release_all_held(struct ofono_voicecall *vc,
+                                       ofono_voicecall_cb_t cb, void *data)
+{
+       calypso_template(vc, "AT+CHLD=0", cb, data);
+}
+
+static void calypso_set_udub(struct ofono_voicecall *vc,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       calypso_template(vc, "AT+CHLD=0", cb, data);
+}
+
+static void calypso_release_all_active(struct ofono_voicecall *vc,
+                                       ofono_voicecall_cb_t cb, void *data)
+{
+       calypso_template(vc, "AT+CHLD=1", cb, data);
+}
+
+static void calypso_release_specific(struct ofono_voicecall *vc, int id,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       char buf[32];
+
+       sprintf(buf, "AT+CHLD=1%d", id);
+       calypso_template(vc, buf, cb, data);
+}
+
+static void calypso_private_chat(struct ofono_voicecall *vc, int id,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       char buf[32];
+
+       sprintf(buf, "AT+CHLD=2%d", id);
+       calypso_template(vc, buf, cb, data);
+}
+
+static void calypso_create_multiparty(struct ofono_voicecall *vc,
+                                       ofono_voicecall_cb_t cb, void *data)
+{
+       calypso_template(vc, "AT+CHLD=3", cb, data);
+}
+
+static void calypso_transfer(struct ofono_voicecall *vc,
+                       ofono_voicecall_cb_t cb, void *data)
+{
+       calypso_template(vc, "AT+CHLD=4", cb, data);
+}
+
+static void calypso_deflect(struct ofono_voicecall *vc,
+                               const struct ofono_phone_number *ph,
+                               ofono_voicecall_cb_t cb, void *data)
+{
+       char buf[128];
+
+       sprintf(buf, "AT+CTFR=%s,%d", ph->number, ph->type);
+       calypso_template(vc, buf, cb, data);
+}
+
+static void calypso_send_dtmf(struct ofono_voicecall *vc, const char *dtmf,
+                       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);
+       int len = strlen(dtmf);
+       int s;
+       int i;
+       char *buf;
+
+       /* strlen("+VTS=\"T\";") = 9 + initial AT + null */
+       buf = g_try_new(char, len * 9 + 3);
+
+       if (!buf) {
+               DECLARE_FAILURE(error);
+               cb(&error, data);
+               return;
+       }
+
+       s = sprintf(buf, "AT+VTS=\"%c\"", dtmf[0]);
+
+       for (i = 1; i < len; i++)
+               s += sprintf(buf + s, ";+VTS=\"%c\"", dtmf[i]);
+
+       calypso_template(vc, buf, cb, data);
+       g_free(buf);
+}
+
+static void cpi_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+       GAtResultIter iter;
+       int id;
+       int msgtype;
+       int direction;
+       int mode;
+       const char *num;
+       int type;
+       int cause;
+       int line = 0;
+       int validity;
+       struct ofono_call call;
+
+       dump_response("cpi_notify", TRUE, result);
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "%CPI:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &id))
+               return;
+
+       /* msgtype
+        * 0 - setup
+        * 1 - disconnect
+        * 2 - alert
+        * 3 - call proceed
+        * 4 - sync
+        * 5 - progress
+        * 6 - connected
+        * 7 - release
+        * 8 - reject
+        * 9 - request (MO Setup)
+        * 10 - hold
+        */
+       if (!g_at_result_iter_next_number(&iter, &msgtype))
+               return;
+
+       /* Skip in-band ring tone notification */
+       if (!g_at_result_iter_skip_next(&iter))
+               return;
+
+       /* Skip traffic channel assignment */
+       if (!g_at_result_iter_skip_next(&iter))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &direction))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &mode))
+               return;
+
+       DBG("id:%d, msgtype:%d, direction:%d, mode:%d",
+               id, msgtype, direction, mode);
+
+       if (!g_at_result_iter_next_string(&iter, &num))
+               return;
+
+       if (strlen(num) > 0) {
+               DBG("Len > 0");
+               validity = 0;
+
+               if (!g_at_result_iter_next_number(&iter, &type))
+                       return;
+
+               DBG("type obtained");
+       } else {
+               DBG("skip next");
+               validity = 2;
+               type = 129;
+
+               if (!g_at_result_iter_skip_next(&iter))
+                       return;
+               DBG("skipped");
+       }
+
+       DBG("num:%s, type:%d", num, type);
+
+       /* Skip alpha field */
+       if (!g_at_result_iter_skip_next(&iter))
+               return;
+
+       g_at_result_iter_next_number(&iter, &cause);
+       g_at_result_iter_next_number(&iter, &line);
+
+       DBG("cause:%d, line:%d", cause, line);
+
+       /* We only care about voice calls here */
+       if (mode != 0)
+               return;
+
+       if (line != 0) {
+               ofono_error("Alternate Line service not yet handled");
+               return;
+       }
+
+       /* Need to send this on the calypso hardware to avoid echo issues */
+       if (msgtype == 3 || msgtype == 4)
+               g_at_chat_send(vd->chat, "AT%N0187", NULL, NULL, NULL, NULL);
+
+       switch (msgtype) {
+       case 0:
+               /* Set call status to incoming */
+               call.status = 4;
+               break;
+       case 2:
+               /* Set call status to alerting */
+               call.status = 3;
+               break;
+       case 3:
+       case 9:
+               /* Set call status to dialing */
+               call.status = 2;
+               break;
+       case 6:
+               /* Set call status to connected */
+               call.status = 0;
+               break;
+       case 10:
+               /* Set call status to held */
+               call.status = 1;
+               break;
+       case 1:
+       case 8:
+               ofono_voicecall_disconnected(vc, id,
+                                       OFONO_DISCONNECT_REASON_UNKNOWN, NULL);
+               return;
+       default:
+               return;
+       };
+
+       call.id = id;
+       call.type = mode;
+       call.direction = direction;
+       strncpy(call.phone_number.number, num,
+               OFONO_MAX_PHONE_NUMBER_LENGTH);
+       call.phone_number.number[OFONO_MAX_PHONE_NUMBER_LENGTH] = '\0';
+       call.phone_number.type = type;
+       call.clip_validity = validity;
+
+       ofono_voicecall_notify(vc, &call);
+}
+
+static void calypso_voicecall_initialized(gboolean ok, GAtResult *result,
+                                       gpointer user_data)
+{
+       struct ofono_voicecall *vc = user_data;
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+
+       ofono_debug("voicecall_init: registering to notifications");
+
+       g_at_chat_register(vd->chat, "%CPI:", cpi_notify, FALSE, vc, NULL);
+
+       ofono_voicecall_register(vc);
+}
+
+static int calypso_voicecall_probe(struct ofono_voicecall *vc, unsigned int vendor,
+                                       void *data)
+{
+       GAtChat *chat = data;
+       struct voicecall_data *vd;
+
+       vd = g_new0(struct voicecall_data, 1);
+       vd->chat = chat;
+
+       ofono_voicecall_set_data(vc, vd);
+
+       g_at_chat_send(chat, "AT+CLIP=1", NULL, NULL, NULL, NULL);
+       g_at_chat_send(chat, "AT+COLP=0", NULL, NULL, NULL, NULL);
+       g_at_chat_send(chat, "AT+CSSN=1,1", NULL, NULL, NULL, NULL);
+       g_at_chat_send(chat, "AT%CPI=3", NULL, NULL, NULL, NULL);
+       g_at_chat_send(chat, "AT+CCWA=1", NULL,
+                               calypso_voicecall_initialized, vc, NULL);
+
+       return 0;
+}
+
+static void calypso_voicecall_remove(struct ofono_voicecall *vc)
+{
+       struct voicecall_data *vd = ofono_voicecall_get_data(vc);
+
+       g_free(vd);
+}
+
+static struct ofono_voicecall_driver driver = {
+       .name                   = "calypsomodem",
+       .probe                  = calypso_voicecall_probe,
+       .remove                 = calypso_voicecall_remove,
+       .dial                   = calypso_dial,
+       .answer                 = calypso_answer,
+       .hangup                 = calypso_hangup,
+       .list_calls             = NULL,
+       .hold_all_active        = calypso_hold_all_active,
+       .release_all_held       = calypso_release_all_held,
+       .set_udub               = calypso_set_udub,
+       .release_all_active     = calypso_release_all_active,
+       .release_specific       = calypso_release_specific,
+       .private_chat           = calypso_private_chat,
+       .create_multiparty      = calypso_create_multiparty,
+       .transfer               = calypso_transfer,
+       .deflect                = calypso_deflect,
+       .swap_without_accept    = NULL,
+       .send_tones             = calypso_send_dtmf
+};
+
+void calypso_voicecall_init()
+{
+       ofono_voicecall_driver_register(&driver);
+}
+
+void calypso_voicecall_exit()
+{
+       ofono_voicecall_driver_unregister(&driver);
+}