Permalink
Cannot retrieve contributors at this time
Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign up| /* | |
| * | |
| * oFono - Open Source Telephony | |
| * | |
| * Copyright (C) 2008-2011 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 <string.h> | |
| #include <stdlib.h> | |
| #include <stdio.h> | |
| #include <errno.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; | |
| 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 (g_at_chat_send(vd->chat, cmd, none_prefix, | |
| calypso_generic_cb, cbd, g_free) > 0) | |
| return; | |
| g_free(cbd); | |
| CALLBACK_WITH_FAILURE(cb, data); | |
| } | |
| static void calypso_dial(struct ofono_voicecall *vc, | |
| const struct ofono_phone_number *ph, | |
| enum ofono_clir_option clir, | |
| ofono_voicecall_cb_t cb, void *data) | |
| { | |
| char buf[256]; | |
| if (ph->type == 145) | |
| snprintf(buf, sizeof(buf), "ATD+%s", ph->number); | |
| else | |
| snprintf(buf, sizeof(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; | |
| } | |
| 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_ath(struct ofono_voicecall *vc, | |
| ofono_voicecall_cb_t cb, void *data) | |
| { | |
| calypso_template(vc, "ATH", cb, data); | |
| } | |
| static void calypso_chup(struct ofono_voicecall *vc, | |
| ofono_voicecall_cb_t cb, void *data) | |
| { | |
| calypso_template(vc, "AT+CHUP", 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]; | |
| /* On calypso, 1X only releases active calls, while 7X releases | |
| * active or held calls | |
| */ | |
| snprintf(buf, sizeof(buf), "AT%%CHLD=7%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]; | |
| snprintf(buf, sizeof(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]; | |
| snprintf(buf, sizeof(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) | |
| { | |
| 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 == NULL) { | |
| CALLBACK_WITH_FAILURE(cb, 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; | |
| 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", none_prefix, | |
| NULL, NULL, NULL); | |
| ofono_call_init(&call); | |
| 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); | |
| DBG("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_try_new0(struct voicecall_data, 1); | |
| if (vd == NULL) | |
| return -ENOMEM; | |
| vd->chat = g_at_chat_clone(chat); | |
| ofono_voicecall_set_data(vc, vd); | |
| g_at_chat_send(vd->chat, "AT%CPI=3", 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); | |
| ofono_voicecall_set_data(vc, NULL); | |
| g_at_chat_unref(vd->chat); | |
| g_free(vd); | |
| } | |
| static const struct ofono_voicecall_driver driver = { | |
| .name = "calypsomodem", | |
| .probe = calypso_voicecall_probe, | |
| .remove = calypso_voicecall_remove, | |
| .dial = calypso_dial, | |
| .answer = calypso_answer, | |
| .hangup_all = calypso_ath, | |
| .hangup_active = calypso_chup, | |
| .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(void) | |
| { | |
| ofono_voicecall_driver_register(&driver); | |
| } | |
| void calypso_voicecall_exit(void) | |
| { | |
| ofono_voicecall_driver_unregister(&driver); | |
| } |