2009-08-24 12:47:17 +00:00
|
|
|
/*
|
|
|
|
*
|
2010-10-14 07:50:08 +00:00
|
|
|
* oFono - Open Source Telephony
|
2009-08-24 12:47:17 +00:00
|
|
|
*
|
2011-10-10 20:39:48 +00:00
|
|
|
* Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies).
|
2009-08-24 12:47:17 +00:00
|
|
|
*
|
2010-10-14 07:50:08 +00:00
|
|
|
* 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.
|
2009-08-24 12:47:17 +00:00
|
|
|
*
|
2010-10-14 07:50:08 +00:00
|
|
|
* 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.
|
2009-08-24 12:47:17 +00:00
|
|
|
*
|
2010-10-14 07:50:08 +00:00
|
|
|
* 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
|
2009-08-24 12:47:17 +00:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#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>
|
2010-11-14 17:31:06 +00:00
|
|
|
#include <gisi/message.h>
|
2009-10-09 14:45:37 +00:00
|
|
|
#include <gisi/iter.h>
|
2009-08-24 12:47:17 +00:00
|
|
|
|
|
|
|
#include <ofono/log.h>
|
|
|
|
#include <ofono/modem.h>
|
|
|
|
#include <ofono/voicecall.h>
|
|
|
|
|
2010-01-08 08:53:42 +00:00
|
|
|
#include "isimodem.h"
|
|
|
|
#include "isiutil.h"
|
|
|
|
#include "call.h"
|
|
|
|
#include "debug.h"
|
2009-08-24 12:47:17 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
#define ISI_CALL_TIMEOUT 1000
|
|
|
|
|
2009-10-09 14:45:37 +00:00
|
|
|
struct isi_call {
|
2010-11-14 17:31:06 +00:00
|
|
|
uint8_t id;
|
|
|
|
uint8_t call_id;
|
|
|
|
uint8_t status;
|
2011-05-23 15:20:11 +00:00
|
|
|
uint8_t prev_status;
|
2010-11-14 17:31:06 +00:00
|
|
|
uint8_t mode;
|
|
|
|
uint8_t mode_info;
|
|
|
|
uint8_t cause_type;
|
|
|
|
uint8_t cause;
|
|
|
|
uint8_t addr_type;
|
|
|
|
uint8_t presentation;
|
2011-02-25 19:23:17 +00:00
|
|
|
uint8_t name_presentation;
|
2009-10-09 14:45:37 +00:00
|
|
|
uint8_t reason;
|
2010-11-14 17:31:06 +00:00
|
|
|
char address[20];
|
2011-02-25 19:23:17 +00:00
|
|
|
char name[20];
|
2010-11-14 17:31:06 +00:00
|
|
|
char addr_pad[4];
|
2009-10-09 14:45:37 +00:00
|
|
|
};
|
2009-08-24 12:47:17 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
struct call_addr_info {
|
|
|
|
uint8_t call_id;
|
|
|
|
uint8_t mode;
|
|
|
|
uint8_t mode_info;
|
|
|
|
uint8_t status;
|
|
|
|
uint8_t filler[2];
|
|
|
|
uint8_t addr_type;
|
|
|
|
uint8_t presentation;
|
|
|
|
uint8_t filler2;
|
|
|
|
uint8_t addr_len;
|
2009-08-24 12:47:17 +00:00
|
|
|
};
|
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
struct call_info {
|
|
|
|
uint8_t call_id;
|
|
|
|
uint8_t mode;
|
|
|
|
uint8_t mode_info;
|
|
|
|
uint8_t status;
|
|
|
|
};
|
2009-10-09 14:45:37 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
struct isi_voicecall {
|
|
|
|
GIsiClient *client;
|
2011-04-12 16:47:27 +00:00
|
|
|
GIsiClient *pn_call;
|
|
|
|
GIsiClient *pn_modem_call;
|
2010-11-14 17:31:06 +00:00
|
|
|
struct isi_call_req_ctx *queue;
|
|
|
|
struct isi_call calls[8];
|
2011-02-23 11:43:21 +00:00
|
|
|
void *control_req_irc;
|
2009-10-09 14:45:37 +00:00
|
|
|
};
|
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
typedef void isi_call_req_step(struct isi_call_req_ctx *ctx, int reason);
|
2009-10-09 14:45:37 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
struct isi_call_req_ctx {
|
|
|
|
struct isi_call_req_ctx *next;
|
|
|
|
struct isi_call_req_ctx **prev;
|
2009-10-09 14:45:37 +00:00
|
|
|
isi_call_req_step *step;
|
|
|
|
struct ofono_voicecall *ovc;
|
|
|
|
ofono_voicecall_cb_t cb;
|
|
|
|
void *data;
|
|
|
|
};
|
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
static struct isi_call_req_ctx *isi_call_req(struct ofono_voicecall *ovc,
|
|
|
|
const void *__restrict req,
|
|
|
|
size_t len,
|
|
|
|
GIsiNotifyFunc handler,
|
|
|
|
ofono_voicecall_cb_t cb,
|
|
|
|
void *data)
|
2009-08-24 12:47:17 +00:00
|
|
|
{
|
2010-11-14 17:31:06 +00:00
|
|
|
struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
|
|
|
|
struct isi_call_req_ctx *irc;
|
2009-10-09 14:45:37 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
irc = g_try_new0(struct isi_call_req_ctx, 1);
|
2010-12-01 13:46:57 +00:00
|
|
|
if (irc == NULL) {
|
2010-11-14 17:31:06 +00:00
|
|
|
CALLBACK_WITH_FAILURE(cb, data);
|
|
|
|
return NULL;
|
|
|
|
}
|
2009-10-09 14:45:37 +00:00
|
|
|
|
2010-12-03 08:30:48 +00:00
|
|
|
irc->ovc = ovc;
|
|
|
|
irc->cb = cb;
|
|
|
|
irc->data = data;
|
2009-10-09 14:45:37 +00:00
|
|
|
|
2011-01-04 15:48:33 +00:00
|
|
|
if (g_isi_client_send(ivc->client, req, len, handler, irc, NULL))
|
|
|
|
return irc;
|
2010-11-14 17:31:06 +00:00
|
|
|
|
2011-01-04 15:48:33 +00:00
|
|
|
g_free(irc);
|
|
|
|
return NULL;
|
2010-11-14 17:31:06 +00:00
|
|
|
}
|
2010-12-03 08:30:48 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
static void isi_ctx_queue(struct isi_call_req_ctx *irc, isi_call_req_step *next)
|
|
|
|
{
|
|
|
|
struct isi_voicecall *ivc;
|
2010-12-03 08:30:48 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
if (irc->prev != NULL) {
|
|
|
|
irc->step = next;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ivc = ofono_voicecall_get_data(irc->ovc);
|
|
|
|
if (ivc->queue) {
|
|
|
|
irc->next = ivc->queue;
|
|
|
|
irc->next->prev = &irc->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
irc->prev = &ivc->queue;
|
|
|
|
ivc->queue = irc;
|
2010-12-03 08:30:48 +00:00
|
|
|
}
|
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
static void isi_ctx_remove(struct isi_call_req_ctx *irc)
|
2010-12-03 08:30:48 +00:00
|
|
|
{
|
2010-12-01 13:46:57 +00:00
|
|
|
if (irc->prev == NULL)
|
2010-11-14 17:31:06 +00:00
|
|
|
return;
|
2010-12-03 08:30:48 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
*irc->prev = irc->next;
|
2010-12-03 08:30:48 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
if (irc->next) {
|
|
|
|
irc->next->prev = irc->prev;
|
|
|
|
irc->next = NULL;
|
|
|
|
}
|
|
|
|
irc->prev = NULL;
|
|
|
|
}
|
2010-12-03 08:30:48 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
static void isi_ctx_free(struct isi_call_req_ctx *irc)
|
|
|
|
{
|
2010-12-01 13:46:57 +00:00
|
|
|
if (irc == NULL)
|
2010-11-14 17:31:06 +00:00
|
|
|
return;
|
2009-10-09 14:45:37 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
isi_ctx_remove(irc);
|
2009-10-09 14:45:37 +00:00
|
|
|
g_free(irc);
|
2010-11-14 17:31:06 +00:00
|
|
|
}
|
2009-10-09 14:45:37 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
static gboolean isi_ctx_return(struct isi_call_req_ctx *irc,
|
|
|
|
enum ofono_error_type type, int error)
|
|
|
|
{
|
2010-12-01 13:46:57 +00:00
|
|
|
if (irc == NULL)
|
2010-11-14 17:31:06 +00:00
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
if (irc->cb) {
|
|
|
|
struct ofono_error e = {
|
|
|
|
.type = type,
|
|
|
|
.error = error
|
|
|
|
};
|
|
|
|
irc->cb(&e, irc->data);
|
|
|
|
}
|
2009-10-09 14:45:37 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
isi_ctx_free(irc);
|
|
|
|
return TRUE;
|
2009-08-24 12:47:17 +00:00
|
|
|
}
|
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
static gboolean isi_ctx_return_failure(struct isi_call_req_ctx *irc)
|
2009-08-24 12:47:17 +00:00
|
|
|
{
|
2010-11-14 17:31:06 +00:00
|
|
|
return isi_ctx_return(irc, OFONO_ERROR_TYPE_FAILURE, 0);
|
|
|
|
}
|
2009-10-09 14:45:37 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
static gboolean isi_ctx_return_success(struct isi_call_req_ctx *irc)
|
|
|
|
{
|
2010-12-01 13:46:57 +00:00
|
|
|
if (irc == NULL || irc->step == NULL)
|
2010-11-14 17:31:06 +00:00
|
|
|
return isi_ctx_return(irc, OFONO_ERROR_TYPE_NO_ERROR, 0);
|
2009-10-09 14:45:37 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
irc->step(irc, 0);
|
|
|
|
return TRUE;
|
2009-08-24 12:47:17 +00:00
|
|
|
}
|
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
/* Decoding subblocks */
|
|
|
|
static void isi_call_any_address_sb_proc(struct isi_voicecall *ivc,
|
|
|
|
struct isi_call *call,
|
|
|
|
GIsiSubBlockIter *sb)
|
2009-08-24 12:47:17 +00:00
|
|
|
{
|
2010-11-14 17:31:06 +00:00
|
|
|
uint8_t type;
|
|
|
|
uint8_t pres;
|
|
|
|
uint8_t len;
|
|
|
|
char *addr;
|
|
|
|
|
2011-02-25 19:23:16 +00:00
|
|
|
if (!g_isi_sb_iter_get_byte(sb, &type, 2))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!g_isi_sb_iter_get_byte(sb, &pres, 3))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!g_isi_sb_iter_get_byte(sb, &len, 5))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!g_isi_sb_iter_get_alpha_tag(sb, &addr, 2 * len, 6))
|
2010-11-14 17:31:06 +00:00
|
|
|
return;
|
2010-05-27 16:12:47 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
call->addr_type = type | 0x80;
|
|
|
|
call->presentation = pres;
|
|
|
|
strncpy(call->address, addr, sizeof(call->address));
|
|
|
|
|
|
|
|
g_free(addr);
|
2009-08-24 12:47:17 +00:00
|
|
|
}
|
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
static void isi_call_origin_address_sb_proc(struct isi_voicecall *ivc,
|
|
|
|
struct isi_call *call,
|
|
|
|
GIsiSubBlockIter *sb)
|
2009-08-24 12:47:17 +00:00
|
|
|
{
|
2010-11-14 17:31:06 +00:00
|
|
|
if (call->address[0] == '\0')
|
|
|
|
isi_call_any_address_sb_proc(ivc, call, sb);
|
2009-08-24 12:47:17 +00:00
|
|
|
}
|
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
static void isi_call_destination_address_sb_proc(struct isi_voicecall *ivc,
|
|
|
|
struct isi_call *call,
|
|
|
|
GIsiSubBlockIter *sb)
|
2009-08-24 12:47:17 +00:00
|
|
|
{
|
2010-11-14 17:31:06 +00:00
|
|
|
if (call->address[0] == '\0')
|
|
|
|
isi_call_any_address_sb_proc(ivc, call, sb);
|
|
|
|
}
|
2009-10-09 14:45:37 +00:00
|
|
|
|
2011-02-25 19:23:17 +00:00
|
|
|
static void isi_call_origin_info_sb_proc(struct isi_voicecall *ivc,
|
|
|
|
struct isi_call *call,
|
|
|
|
GIsiSubBlockIter *sb)
|
|
|
|
{
|
|
|
|
uint8_t pres;
|
|
|
|
uint8_t id;
|
|
|
|
uint8_t len;
|
|
|
|
char *name;
|
|
|
|
|
|
|
|
if (!g_isi_sb_iter_get_byte(sb, &pres, 2))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!g_isi_sb_iter_get_byte(sb, &id, 6))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!g_isi_sb_iter_get_byte(sb, &len, 7))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!g_isi_sb_iter_get_alpha_tag(sb, &name, 2 * len, 8))
|
|
|
|
return;
|
|
|
|
|
|
|
|
DBG("Got name %s", name);
|
|
|
|
call->name_presentation = pres;
|
|
|
|
strncpy(call->name, name, sizeof(call->name));
|
|
|
|
|
|
|
|
g_free(name);
|
|
|
|
}
|
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
static void isi_call_mode_sb_proc(struct isi_voicecall *ivc,
|
|
|
|
struct isi_call *call,
|
|
|
|
GIsiSubBlockIter *sb)
|
|
|
|
{
|
|
|
|
uint8_t mode;
|
|
|
|
uint8_t info;
|
2009-10-09 14:45:37 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
if (!g_isi_sb_iter_get_byte(sb, &mode, 2) ||
|
|
|
|
!g_isi_sb_iter_get_byte(sb, &info, 3))
|
|
|
|
return;
|
2009-10-09 14:45:37 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
call->mode = mode;
|
|
|
|
call->mode_info = info;
|
2009-08-24 12:47:17 +00:00
|
|
|
}
|
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
static void isi_call_cause_sb_proc(struct isi_voicecall *ivc,
|
|
|
|
struct isi_call *call,
|
|
|
|
GIsiSubBlockIter *sb)
|
2009-10-09 14:45:37 +00:00
|
|
|
{
|
2010-11-14 17:31:06 +00:00
|
|
|
uint8_t type;
|
|
|
|
uint8_t cause;
|
|
|
|
|
|
|
|
if (!g_isi_sb_iter_get_byte(sb, &type, 2) ||
|
|
|
|
!g_isi_sb_iter_get_byte(sb, &cause, 3))
|
|
|
|
return;
|
|
|
|
|
|
|
|
call->cause_type = type;
|
|
|
|
call->cause = cause;
|
2009-10-09 14:45:37 +00:00
|
|
|
}
|
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
static void isi_call_status_sb_proc(struct isi_voicecall *ivc,
|
|
|
|
struct isi_call *call,
|
|
|
|
GIsiSubBlockIter *sb)
|
2009-10-09 14:45:37 +00:00
|
|
|
{
|
2010-11-14 17:31:06 +00:00
|
|
|
uint8_t status;
|
|
|
|
|
|
|
|
if (!g_isi_sb_iter_get_byte(sb, &status, 2))
|
|
|
|
return;
|
2011-05-23 15:20:11 +00:00
|
|
|
call->prev_status = call->status;
|
2010-11-14 17:31:06 +00:00
|
|
|
call->status = status;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct isi_call *isi_call_status_info_sb_proc(struct isi_voicecall *ivc,
|
|
|
|
GIsiSubBlockIter *sb)
|
|
|
|
{
|
|
|
|
struct isi_call *call = NULL;
|
|
|
|
int i;
|
|
|
|
struct call_info *ci;
|
|
|
|
size_t len = sizeof(struct call_info);
|
|
|
|
|
2011-01-03 15:45:56 +00:00
|
|
|
if (!g_isi_sb_iter_get_struct(sb, (void *) &ci, len, 2))
|
2010-11-14 17:31:06 +00:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
i = ci->call_id & 7;
|
|
|
|
|
|
|
|
if (1 <= i && i <= 7) {
|
|
|
|
call = &ivc->calls[i];
|
|
|
|
call->call_id = ci->call_id;
|
|
|
|
call->status = ci->status;
|
|
|
|
call->mode = ci->mode;
|
|
|
|
call->mode_info = ci->mode_info;
|
2009-10-09 14:45:37 +00:00
|
|
|
}
|
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
return call;
|
2009-10-09 14:45:37 +00:00
|
|
|
}
|
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
static struct isi_call *isi_call_addr_and_status_info_sb_proc(
|
|
|
|
struct isi_voicecall *ivc,
|
|
|
|
GIsiSubBlockIter *sb)
|
|
|
|
{
|
|
|
|
struct isi_call *call = NULL;
|
|
|
|
int i;
|
|
|
|
struct call_addr_info *ci;
|
|
|
|
size_t len = sizeof(struct call_addr_info);
|
|
|
|
char *addr;
|
|
|
|
|
2011-01-03 15:45:56 +00:00
|
|
|
if (!g_isi_sb_iter_get_struct(sb, (void *) &ci, len, 2))
|
2010-11-14 17:31:06 +00:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (!g_isi_sb_iter_get_alpha_tag(sb, &addr, 2 * ci->addr_len, 12))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
i = ci->call_id & 7;
|
|
|
|
|
|
|
|
if (1 <= i && i <= 7) {
|
|
|
|
call = &ivc->calls[i];
|
|
|
|
call->call_id = ci->call_id;
|
|
|
|
call->status = ci->status;
|
|
|
|
call->mode = ci->mode;
|
|
|
|
call->mode_info = ci->mode_info;
|
|
|
|
call->addr_type = ci->addr_type | 0x80;
|
|
|
|
call->presentation = ci->presentation;
|
|
|
|
strncpy(call->address, addr, sizeof call->address);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_free(addr);
|
|
|
|
return call;
|
|
|
|
}
|
2010-12-03 08:30:47 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
static int isi_call_status_to_clcc(const struct isi_call *call)
|
2010-12-03 08:30:47 +00:00
|
|
|
{
|
|
|
|
switch (call->status) {
|
|
|
|
case CALL_STATUS_CREATE:
|
2010-11-14 17:31:06 +00:00
|
|
|
return 2;
|
2010-12-03 08:30:47 +00:00
|
|
|
|
|
|
|
case CALL_STATUS_COMING:
|
2010-11-14 17:31:06 +00:00
|
|
|
return 4;
|
2010-12-03 08:30:47 +00:00
|
|
|
|
|
|
|
case CALL_STATUS_PROCEEDING:
|
2010-11-14 17:31:06 +00:00
|
|
|
|
2010-12-03 08:30:47 +00:00
|
|
|
if ((call->mode_info & CALL_MODE_ORIGINATOR))
|
2010-11-14 17:31:06 +00:00
|
|
|
return 4; /* MT */
|
2010-12-03 08:30:47 +00:00
|
|
|
else
|
2010-11-14 17:31:06 +00:00
|
|
|
return 2; /* MO */
|
2010-12-03 08:30:47 +00:00
|
|
|
|
|
|
|
case CALL_STATUS_MO_ALERTING:
|
2010-11-14 17:31:06 +00:00
|
|
|
return 3;
|
2010-12-03 08:30:47 +00:00
|
|
|
|
|
|
|
case CALL_STATUS_MT_ALERTING:
|
2010-11-14 17:31:06 +00:00
|
|
|
return 4;
|
2010-12-03 08:30:47 +00:00
|
|
|
|
|
|
|
case CALL_STATUS_WAITING:
|
2010-11-14 17:31:06 +00:00
|
|
|
return 5;
|
2010-12-03 08:30:47 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
case CALL_STATUS_MO_RELEASE:
|
2011-02-01 22:22:09 +00:00
|
|
|
return 6;
|
|
|
|
|
2011-05-23 15:20:11 +00:00
|
|
|
case CALL_STATUS_MT_RELEASE:
|
|
|
|
if ((call->prev_status == CALL_STATUS_MT_ALERTING) ||
|
|
|
|
(call->prev_status == CALL_STATUS_COMING) ||
|
|
|
|
(call->prev_status == CALL_STATUS_WAITING))
|
|
|
|
return 4;
|
|
|
|
else
|
|
|
|
return 6;
|
|
|
|
|
2011-02-01 22:22:09 +00:00
|
|
|
case CALL_STATUS_ACTIVE:
|
2010-12-03 08:30:47 +00:00
|
|
|
case CALL_STATUS_HOLD_INITIATED:
|
2010-11-14 17:31:06 +00:00
|
|
|
return 0;
|
2010-12-03 08:30:47 +00:00
|
|
|
|
|
|
|
case CALL_STATUS_HOLD:
|
|
|
|
case CALL_STATUS_RETRIEVE_INITIATED:
|
2010-11-14 17:31:06 +00:00
|
|
|
return 1;
|
2010-12-03 08:30:47 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
case CALL_STATUS_RECONNECT_PENDING:
|
|
|
|
case CALL_STATUS_SWAP_INITIATED:
|
2011-02-01 22:22:09 +00:00
|
|
|
default:
|
2010-11-14 17:31:06 +00:00
|
|
|
return 0;
|
2010-12-03 08:30:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
static struct ofono_call isi_call_as_ofono_call(const struct isi_call *call)
|
2010-12-03 08:30:47 +00:00
|
|
|
{
|
2011-02-01 17:12:49 +00:00
|
|
|
struct ofono_call ocall;
|
2010-12-03 08:30:47 +00:00
|
|
|
struct ofono_phone_number *number = &ocall.phone_number;
|
|
|
|
|
2011-02-01 17:12:49 +00:00
|
|
|
ofono_call_init(&ocall);
|
|
|
|
ocall.id = call->id;
|
2010-12-03 08:30:47 +00:00
|
|
|
ocall.type = 0; /* Voice call */
|
|
|
|
ocall.direction = call->mode_info & CALL_MODE_ORIGINATOR;
|
2010-11-14 17:31:06 +00:00
|
|
|
ocall.status = isi_call_status_to_clcc(call);
|
|
|
|
|
2010-12-03 08:30:47 +00:00
|
|
|
memcpy(number->number, call->address, sizeof(number->number));
|
2011-02-25 19:23:17 +00:00
|
|
|
memcpy(ocall.name, call->name, sizeof(ocall.name));
|
2010-11-14 17:31:06 +00:00
|
|
|
|
2010-12-03 08:30:47 +00:00
|
|
|
number->type = 0x80 | call->addr_type;
|
|
|
|
ocall.clip_validity = call->presentation & 3;
|
2011-02-25 19:23:17 +00:00
|
|
|
ocall.cnap_validity = call->name_presentation & 3;
|
2010-12-03 08:30:47 +00:00
|
|
|
|
|
|
|
if (ocall.clip_validity == 0 && strlen(number->number) == 0)
|
|
|
|
ocall.clip_validity = 2;
|
|
|
|
|
2011-02-25 19:23:17 +00:00
|
|
|
if (ocall.cnap_validity == 0 && strlen(call->name) == 0)
|
|
|
|
ocall.cnap_validity = 2;
|
|
|
|
|
2010-12-03 08:30:47 +00:00
|
|
|
return ocall;
|
|
|
|
}
|
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
static gboolean check_response_status(const GIsiMessage *msg, uint8_t msgid)
|
|
|
|
{
|
|
|
|
if (g_isi_msg_error(msg) < 0) {
|
|
|
|
DBG("Error: %s", strerror(-g_isi_msg_error(msg)));
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (g_isi_msg_id(msg) != msgid) {
|
|
|
|
DBG("Unexpected msg: %s",
|
|
|
|
net_message_id_name(g_isi_msg_id(msg)));
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2010-12-03 08:30:47 +00:00
|
|
|
static struct isi_call *isi_call_set_idle(struct isi_call *call)
|
|
|
|
{
|
|
|
|
uint8_t id;
|
|
|
|
|
2010-12-01 13:46:57 +00:00
|
|
|
if (call == NULL)
|
2010-11-14 17:31:06 +00:00
|
|
|
return NULL;
|
|
|
|
|
2010-12-03 08:30:47 +00:00
|
|
|
id = call->id;
|
2010-11-14 17:31:06 +00:00
|
|
|
memset(call, 0, sizeof(struct isi_call));
|
2010-12-03 08:30:47 +00:00
|
|
|
call->id = id;
|
|
|
|
|
|
|
|
return call;
|
|
|
|
}
|
|
|
|
|
2011-02-01 22:22:09 +00:00
|
|
|
static void isi_call_disconnected(struct ofono_voicecall *ovc,
|
|
|
|
struct isi_call *call)
|
2010-12-03 08:30:47 +00:00
|
|
|
{
|
2010-11-14 17:31:06 +00:00
|
|
|
struct ofono_error error = {
|
|
|
|
OFONO_ERROR_TYPE_NO_ERROR, 0
|
|
|
|
};
|
2011-02-01 22:22:09 +00:00
|
|
|
|
|
|
|
DBG("disconnected id=%u reason=%u", call->id, call->reason);
|
|
|
|
|
|
|
|
ofono_voicecall_disconnected(ovc, call->id, call->reason, &error);
|
|
|
|
|
|
|
|
isi_call_set_idle(call);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void isi_call_set_disconnect_reason(struct isi_call *call)
|
|
|
|
{
|
2010-11-14 17:31:06 +00:00
|
|
|
enum ofono_disconnect_reason reason;
|
2010-12-03 08:30:47 +00:00
|
|
|
|
2011-02-01 22:22:09 +00:00
|
|
|
if (call->reason != OFONO_DISCONNECT_REASON_UNKNOWN)
|
|
|
|
return;
|
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
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:
|
2010-12-03 08:30:47 +00:00
|
|
|
reason = OFONO_DISCONNECT_REASON_ERROR;
|
2010-11-14 17:31:06 +00:00
|
|
|
}
|
|
|
|
|
2011-02-01 22:22:09 +00:00
|
|
|
call->reason = reason;
|
2010-12-03 08:30:47 +00:00
|
|
|
}
|
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
static void isi_call_notify(struct ofono_voicecall *ovc, struct isi_call *call)
|
2010-12-03 08:30:47 +00:00
|
|
|
{
|
|
|
|
struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
|
2010-11-14 17:31:06 +00:00
|
|
|
struct isi_call_req_ctx *irc, **queue;
|
2010-12-03 08:30:47 +00:00
|
|
|
struct ofono_call ocall;
|
|
|
|
|
|
|
|
DBG("called with status=%s (0x%02X)",
|
|
|
|
call_status_name(call->status), call->status);
|
|
|
|
|
|
|
|
for (queue = &ivc->queue; (irc = *queue);) {
|
2010-11-14 17:31:06 +00:00
|
|
|
irc->step(irc, call->status);
|
2010-12-03 08:30:47 +00:00
|
|
|
|
|
|
|
if (*queue == irc)
|
|
|
|
queue = &irc->next;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (call->status) {
|
2010-11-14 17:31:06 +00:00
|
|
|
case CALL_STATUS_IDLE:
|
2011-02-01 22:22:09 +00:00
|
|
|
isi_call_disconnected(ovc, call);
|
|
|
|
return;
|
|
|
|
|
|
|
|
case CALL_STATUS_COMING:
|
|
|
|
case CALL_STATUS_PROCEEDING:
|
|
|
|
if ((call->mode_info & CALL_MODE_ORIGINATOR))
|
|
|
|
/* Do not notify early MT calls */
|
|
|
|
return;
|
|
|
|
break;
|
|
|
|
|
2010-12-03 08:30:47 +00:00
|
|
|
case CALL_STATUS_MO_RELEASE:
|
|
|
|
case CALL_STATUS_MT_RELEASE:
|
2011-05-23 15:20:11 +00:00
|
|
|
/*
|
|
|
|
* Core requires the call status to be either incoming
|
|
|
|
* or waiting to identify the disconnected call as missed.
|
|
|
|
* The MT RELEASE is not mapped to any state in +CLCC, but
|
|
|
|
* we need the disconnect reason.
|
|
|
|
*/
|
2011-02-01 22:22:09 +00:00
|
|
|
isi_call_set_disconnect_reason(call);
|
2011-04-05 09:32:53 +00:00
|
|
|
break;
|
2011-05-23 15:20:11 +00:00
|
|
|
case CALL_STATUS_TERMINATED:
|
|
|
|
DBG("State( CALL_STATUS_TERMINATED ) need not be reported to Core");
|
|
|
|
/*
|
|
|
|
* The call terminated is not reported to core as
|
|
|
|
* these intermediate states are not processed in
|
|
|
|
* the core. We report the call status when it becomes
|
|
|
|
* idle and TERMINATED is not mapped to +CLCC. The disconnect
|
|
|
|
* reason is set, so that the call termination cause
|
|
|
|
* in case of error is available to the core.
|
|
|
|
*/
|
|
|
|
isi_call_set_disconnect_reason(call);
|
|
|
|
return;
|
2011-04-05 09:32:53 +00:00
|
|
|
case CALL_STATUS_ANSWERED:
|
|
|
|
DBG("State need not be reported to Core");
|
|
|
|
return;
|
2010-11-14 17:31:06 +00:00
|
|
|
}
|
2009-10-09 14:45:37 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
ocall = isi_call_as_ofono_call(call);
|
2009-10-09 14:45:37 +00:00
|
|
|
|
2011-02-25 19:23:17 +00:00
|
|
|
DBG("id=%u,%s,%u,\"%s\",\"%s\",%u,%u",
|
2010-11-14 17:31:06 +00:00
|
|
|
ocall.id,
|
|
|
|
ocall.direction ? "terminated" : "originated",
|
|
|
|
ocall.status,
|
|
|
|
ocall.phone_number.number,
|
2011-02-25 19:23:17 +00:00
|
|
|
ocall.name,
|
2010-11-14 17:31:06 +00:00
|
|
|
ocall.phone_number.type,
|
|
|
|
ocall.clip_validity);
|
2009-10-09 14:45:37 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
ofono_voicecall_notify(ovc, &ocall);
|
2009-08-24 12:47:17 +00:00
|
|
|
}
|
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
static void isi_call_create_resp(const GIsiMessage *msg, void *data)
|
2009-10-09 14:45:37 +00:00
|
|
|
{
|
2010-11-14 17:31:06 +00:00
|
|
|
struct isi_call_req_ctx *irc = data;
|
2009-10-09 14:45:37 +00:00
|
|
|
uint8_t call_id;
|
2011-04-12 14:33:29 +00:00
|
|
|
uint8_t subblocks;
|
|
|
|
|
|
|
|
if (!check_response_status(msg, CALL_CREATE_RESP))
|
|
|
|
goto failure;
|
|
|
|
|
|
|
|
if (!g_isi_msg_data_get_byte(msg, 0, &call_id) ||
|
|
|
|
call_id == CALL_ID_NONE)
|
|
|
|
goto failure;
|
|
|
|
|
|
|
|
if (!g_isi_msg_data_get_byte(msg, 1, &subblocks))
|
|
|
|
goto failure;
|
|
|
|
|
|
|
|
if (subblocks != 0) {
|
|
|
|
GIsiSubBlockIter iter;
|
|
|
|
struct isi_call call = { 0 };
|
|
|
|
|
|
|
|
for (g_isi_sb_iter_init(&iter, msg, 2);
|
|
|
|
g_isi_sb_iter_is_valid(&iter);
|
|
|
|
g_isi_sb_iter_next(&iter)) {
|
|
|
|
|
|
|
|
switch (g_isi_sb_iter_get_id(&iter)) {
|
|
|
|
case CALL_CAUSE:
|
|
|
|
isi_call_cause_sb_proc(NULL, &call, &iter);
|
|
|
|
DBG("CALL_CREATE_RESP "
|
|
|
|
"cause_type=0x%02x cause=0x%02x",
|
|
|
|
call.cause_type, call.cause);
|
|
|
|
goto failure;
|
|
|
|
}
|
|
|
|
}
|
2009-10-09 14:45:37 +00:00
|
|
|
}
|
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
isi_ctx_return_success(irc);
|
2011-04-12 14:33:29 +00:00
|
|
|
return;
|
|
|
|
|
|
|
|
failure:
|
|
|
|
isi_ctx_return_failure(irc);
|
2009-10-09 14:45:37 +00:00
|
|
|
}
|
|
|
|
|
2011-03-03 07:39:29 +00:00
|
|
|
static struct isi_call_req_ctx *isi_modem_call_create_req(
|
|
|
|
struct ofono_voicecall *ovc,
|
|
|
|
uint8_t presentation,
|
|
|
|
uint8_t addr_type,
|
|
|
|
char const address[21],
|
|
|
|
ofono_voicecall_cb_t cb,
|
|
|
|
void *data)
|
|
|
|
{
|
|
|
|
size_t addr_len = strlen(address);
|
|
|
|
size_t sub_len = ALIGN4(6 + 2 * addr_len);
|
|
|
|
size_t offset = 3 + 4 + 4 + 6;
|
|
|
|
uint8_t req[3 + 4 + 4 + 6 + 40] = {
|
|
|
|
CALL_CREATE_REQ,
|
|
|
|
0, /* No id */
|
|
|
|
3, /* Mode, Clir, Number */
|
|
|
|
CALL_MODE, 4, CALL_MODE_SPEECH, 0,
|
|
|
|
CALL_LINE_ID, 4, presentation, 0,
|
|
|
|
CALL_DESTINATION_ADDRESS, sub_len, addr_type & 0x7F, 0, 0,
|
|
|
|
addr_len,
|
|
|
|
/* uint16_t addr[20] */
|
|
|
|
};
|
|
|
|
size_t rlen = 3 + 4 + 4 + sub_len;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
static struct isi_call_req_ctx *isi_call_create_req(struct ofono_voicecall *ovc,
|
|
|
|
uint8_t presentation,
|
|
|
|
uint8_t addr_type,
|
|
|
|
char const address[21],
|
|
|
|
ofono_voicecall_cb_t cb,
|
|
|
|
void *data)
|
2009-10-09 14:45:37 +00:00
|
|
|
{
|
|
|
|
size_t addr_len = strlen(address);
|
2011-03-03 07:39:29 +00:00
|
|
|
size_t sub_len = ALIGN4(6 + 2 * addr_len);
|
|
|
|
size_t offset = 3 + 4 + 8 + 6;
|
|
|
|
uint8_t req[3 + 4 + 8 + 6 + 40] = {
|
|
|
|
CALL_CREATE_REQ,
|
|
|
|
0, /* No id */
|
|
|
|
3, /* Mode, Clir, Number */
|
|
|
|
CALL_MODE, 4, CALL_MODE_SPEECH, CALL_MODE_INFO_NONE,
|
|
|
|
CALL_ORIGIN_INFO, 8, presentation, 0, 0, 0, 0, 0,
|
|
|
|
CALL_DESTINATION_ADDRESS, sub_len, addr_type & 0x7F, 0, 0,
|
|
|
|
addr_len,
|
2011-02-23 11:43:21 +00:00
|
|
|
/* uint16_t addr[20] */
|
2011-03-03 07:39:29 +00:00
|
|
|
};
|
|
|
|
size_t rlen = 3 + 4 + 8 + sub_len;
|
|
|
|
size_t i;
|
2009-10-09 14:45:37 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
static void isi_call_status_ind_cb(const GIsiMessage *msg, void *data)
|
2009-10-09 14:45:37 +00:00
|
|
|
{
|
2010-11-14 17:31:06 +00:00
|
|
|
struct ofono_voicecall *ovc = data;
|
2009-10-09 14:45:37 +00:00
|
|
|
struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
|
|
|
|
struct isi_call *call;
|
2010-11-14 17:31:06 +00:00
|
|
|
GIsiSubBlockIter iter;
|
2009-10-09 14:45:37 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
uint8_t call_id;
|
|
|
|
uint8_t old_status;
|
2009-10-09 14:45:37 +00:00
|
|
|
|
2010-12-01 13:46:57 +00:00
|
|
|
if (ivc == NULL || g_isi_msg_id(msg) != CALL_STATUS_IND ||
|
2010-11-14 17:31:06 +00:00
|
|
|
!g_isi_msg_data_get_byte(msg, 0, &call_id) ||
|
|
|
|
(call_id & 7) == 0)
|
2009-10-09 14:45:37 +00:00
|
|
|
return;
|
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
call = &ivc->calls[call_id & 7];
|
|
|
|
old_status = call->status;
|
|
|
|
call->call_id = call_id;
|
2009-10-09 14:45:37 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
for (g_isi_sb_iter_init(&iter, msg, 2);
|
|
|
|
g_isi_sb_iter_is_valid(&iter);
|
|
|
|
g_isi_sb_iter_next(&iter)) {
|
2009-10-09 14:45:37 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
switch (g_isi_sb_iter_get_id(&iter)) {
|
2009-10-09 14:45:37 +00:00
|
|
|
case CALL_STATUS:
|
2010-11-14 17:31:06 +00:00
|
|
|
isi_call_status_sb_proc(ivc, call, &iter);
|
2009-10-09 14:45:37 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case CALL_MODE:
|
2010-11-14 17:31:06 +00:00
|
|
|
isi_call_mode_sb_proc(ivc, call, &iter);
|
2009-10-09 14:45:37 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case CALL_CAUSE:
|
2010-11-14 17:31:06 +00:00
|
|
|
isi_call_cause_sb_proc(ivc, call, &iter);
|
2009-10-09 14:45:37 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case CALL_DESTINATION_ADDRESS:
|
2010-11-14 17:31:06 +00:00
|
|
|
isi_call_destination_address_sb_proc(ivc, call, &iter);
|
2009-10-09 14:45:37 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case CALL_ORIGIN_ADDRESS:
|
2010-11-14 17:31:06 +00:00
|
|
|
isi_call_origin_address_sb_proc(ivc, call, &iter);
|
2009-10-09 14:45:37 +00:00
|
|
|
break;
|
|
|
|
|
2011-02-25 19:23:17 +00:00
|
|
|
case CALL_ORIGIN_INFO:
|
|
|
|
isi_call_origin_info_sb_proc(ivc, call, &iter);
|
|
|
|
break;
|
|
|
|
|
2009-10-09 14:45:37 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-02-01 22:22:09 +00:00
|
|
|
if (old_status == call->status)
|
|
|
|
return;
|
2009-10-09 14:45:37 +00:00
|
|
|
|
2011-02-01 22:22:09 +00:00
|
|
|
isi_call_notify(ovc, call);
|
|
|
|
}
|
2009-10-09 14:45:37 +00:00
|
|
|
|
2011-02-01 22:22:09 +00:00
|
|
|
static void isi_call_terminated_ind_cb(const GIsiMessage *msg, void *data)
|
|
|
|
{
|
|
|
|
struct ofono_voicecall *ovc = data;
|
|
|
|
struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
|
|
|
|
struct isi_call *call;
|
2010-11-14 17:31:06 +00:00
|
|
|
|
2011-02-01 22:22:09 +00:00
|
|
|
uint8_t call_id;
|
|
|
|
uint8_t old_status;
|
|
|
|
|
|
|
|
if (ivc == NULL || g_isi_msg_id(msg) != CALL_TERMINATED_IND ||
|
|
|
|
!g_isi_msg_data_get_byte(msg, 0, &call_id) ||
|
|
|
|
(call_id & 7) == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
call = &ivc->calls[call_id & 7];
|
|
|
|
old_status = call->status;
|
|
|
|
|
|
|
|
if (old_status == CALL_STATUS_IDLE)
|
|
|
|
return;
|
|
|
|
|
|
|
|
call->status = CALL_STATUS_TERMINATED;
|
2010-11-14 17:31:06 +00:00
|
|
|
isi_call_notify(ovc, call);
|
2009-10-09 14:45:37 +00:00
|
|
|
}
|
|
|
|
|
2011-03-04 17:12:56 +00:00
|
|
|
static gboolean decode_notify(GIsiSubBlockIter *iter)
|
|
|
|
{
|
|
|
|
uint8_t byte;
|
|
|
|
|
|
|
|
if (!g_isi_sb_iter_get_byte(iter, &byte, 2))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
switch (byte) {
|
|
|
|
case CALL_NOTIFY_USER_SUSPENDED:
|
|
|
|
DBG("CALL_NOTIFY_USER_SUSPENDED");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CALL_NOTIFY_USER_RESUMED:
|
|
|
|
DBG("CALL_NOTIFY_USER_RESUMED");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CALL_NOTIFY_BEARER_CHANGE:
|
|
|
|
DBG("CALL_NOTIFY_BEARER_CHANGE");
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
DBG("Unknown notification: 0x%02X", byte);
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean decode_ss_code(GIsiSubBlockIter *iter, int *cssi, int *cssu)
|
|
|
|
{
|
|
|
|
uint16_t word;
|
|
|
|
|
|
|
|
if (!g_isi_sb_iter_get_word(iter, &word, 2))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
switch (word) {
|
|
|
|
case CALL_SSC_ALL_FWDS:
|
|
|
|
DBG("Call forwarding is active");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CALL_SSC_ALL_COND_FWD:
|
|
|
|
*cssi = SS_MO_CONDITIONAL_FORWARDING;
|
|
|
|
DBG("Some of conditional call forwardings active");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CALL_SSC_CFU:
|
|
|
|
*cssi = SS_MO_UNCONDITIONAL_FORWARDING;
|
|
|
|
DBG("Unconditional call forwarding is active");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CALL_SSC_OUTGOING_BARR_SERV:
|
|
|
|
*cssi = SS_MO_OUTGOING_BARRING;
|
|
|
|
DBG("Outgoing calls are barred");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CALL_SSC_INCOMING_BARR_SERV:
|
|
|
|
*cssi = SS_MO_INCOMING_BARRING;
|
|
|
|
DBG("Incoming calls are barred");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CALL_SSC_CALL_WAITING:
|
|
|
|
DBG("Incoming calls are barred");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CALL_SSC_CLIR:
|
|
|
|
DBG("CLIR connected unknown indication.");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CALL_SSC_MPTY:
|
|
|
|
*cssu = SS_MT_MULTIPARTY_VOICECALL;
|
|
|
|
DBG("Multiparty call entered.");
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CALL_SSC_CALL_HOLD:
|
|
|
|
*cssu = SS_MT_VOICECALL_HOLD_RELEASED;
|
|
|
|
DBG("Call on hold has been released.");
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
DBG("Unknown/unhandled notification: 0x%02X", word);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean decode_ss_status(GIsiSubBlockIter *iter)
|
|
|
|
{
|
|
|
|
uint8_t byte;
|
|
|
|
|
|
|
|
if (!g_isi_sb_iter_get_byte(iter, &byte, 2))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (byte & CALL_SS_STATUS_ACTIVE)
|
|
|
|
DBG("CALL_SS_STATUS_ACTIVE");
|
|
|
|
|
|
|
|
if (byte & CALL_SS_STATUS_REGISTERED)
|
|
|
|
DBG("CALL_SS_STATUS_REGISTERED");
|
|
|
|
|
|
|
|
if (byte & CALL_SS_STATUS_PROVISIONED)
|
|
|
|
DBG("CALL_SS_STATUS_PROVISIONED");
|
|
|
|
|
|
|
|
if (byte & CALL_SS_STATUS_QUIESCENT)
|
|
|
|
DBG("CALL_SS_STATUS_QUIESCENT");
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean decode_ss_notify(GIsiSubBlockIter *iter, int *cssi, int *cssu)
|
|
|
|
{
|
|
|
|
uint8_t byte;
|
|
|
|
|
|
|
|
if (!g_isi_sb_iter_get_byte(iter, &byte, 2))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (byte & CALL_SSN_INCOMING_IS_FWD) {
|
|
|
|
*cssu = SS_MT_CALL_FORWARDED;
|
|
|
|
DBG("This is a forwarded call #1.");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (byte & CALL_SSN_INCOMING_FWD)
|
|
|
|
DBG("This is a forwarded call #2.");
|
|
|
|
|
|
|
|
if (byte & CALL_SSN_OUTGOING_FWD) {
|
|
|
|
*cssi = SS_MO_CALL_FORWARDED;
|
|
|
|
DBG("Call has been forwarded.");
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean decode_ss_notify_indicator(GIsiSubBlockIter *iter, int *cssi)
|
|
|
|
{
|
|
|
|
uint8_t byte;
|
|
|
|
|
|
|
|
if (!g_isi_sb_iter_get_byte(iter, &byte, 2))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (byte & CALL_SSI_CALL_IS_WAITING) {
|
|
|
|
*cssi = SS_MO_CALL_WAITING;
|
|
|
|
DBG("Call is waiting.");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (byte & CALL_SSI_MPTY)
|
|
|
|
DBG("Multiparty call");
|
|
|
|
|
|
|
|
if (byte & CALL_SSI_CLIR_SUPPR_REJ) {
|
|
|
|
*cssi = SS_MO_CLIR_SUPPRESSION_REJECTED;
|
|
|
|
DBG("CLIR suppression rejected");
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean decode_ss_hold_indicator(GIsiSubBlockIter *iter, int *cssu)
|
|
|
|
{
|
|
|
|
uint8_t byte;
|
|
|
|
|
|
|
|
if (!g_isi_sb_iter_get_byte(iter, &byte, 2))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (byte == CALL_HOLD_IND_RETRIEVED) {
|
|
|
|
*cssu = SS_MT_VOICECALL_RETRIEVED;
|
|
|
|
DBG("Call has been retrieved");
|
|
|
|
} else if (byte & CALL_HOLD_IND_ON_HOLD) {
|
|
|
|
*cssu = SS_MT_VOICECALL_ON_HOLD;
|
|
|
|
DBG("Call has been put on hold");
|
|
|
|
} else {
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean decode_ss_ect_indicator(GIsiSubBlockIter *iter, int *cssu)
|
|
|
|
{
|
|
|
|
uint8_t byte;
|
|
|
|
|
|
|
|
if (!g_isi_sb_iter_get_byte(iter, &byte, 2))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (byte & CALL_ECT_CALL_STATE_ALERT) {
|
|
|
|
*cssu = SS_MT_VOICECALL_IN_TRANSFER;
|
|
|
|
DBG("Call is being connected with the remote party in "
|
|
|
|
"alerting state");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (byte & CALL_ECT_CALL_STATE_ACTIVE) {
|
|
|
|
*cssu = SS_MT_VOICECALL_TRANSFERRED;
|
|
|
|
DBG("Call has been connected with the other remote "
|
|
|
|
"party in explicit call transfer operation.");
|
|
|
|
}
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean decode_remote_address(GIsiSubBlockIter *iter,
|
|
|
|
struct ofono_phone_number *number,
|
|
|
|
int *index)
|
|
|
|
{
|
|
|
|
uint8_t type, len;
|
|
|
|
char *addr;
|
|
|
|
|
|
|
|
if (!g_isi_sb_iter_get_byte(iter, &type, 2))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (!g_isi_sb_iter_get_byte(iter, &len, 5))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (len > OFONO_MAX_PHONE_NUMBER_LENGTH)
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (!g_isi_sb_iter_get_alpha_tag(iter, &addr, 2 * len, 6))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
strncpy(number->number, addr, len);
|
|
|
|
number->number[OFONO_MAX_PHONE_NUMBER_LENGTH] = '\0';
|
|
|
|
number->type = type;
|
|
|
|
|
|
|
|
g_free(addr);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean decode_cug_info(GIsiSubBlockIter *iter, int *index, int *cssu)
|
|
|
|
{
|
|
|
|
uint8_t pref;
|
|
|
|
uint8_t access;
|
|
|
|
uint16_t word;
|
|
|
|
|
|
|
|
if (!g_isi_sb_iter_get_byte(iter, &pref, 2))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (!g_isi_sb_iter_get_byte(iter, &access, 3))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (!g_isi_sb_iter_get_word(iter, &word, 4))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
DBG("Preferential CUG: 0x%02X", pref);
|
|
|
|
DBG("CUG output access: 0x%02X", access);
|
|
|
|
|
|
|
|
*index = word;
|
|
|
|
*cssu = SS_MO_CUG_CALL;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void notification_ind_cb(const GIsiMessage *msg, void *data)
|
|
|
|
{
|
|
|
|
struct ofono_voicecall *ovc = data;
|
|
|
|
GIsiSubBlockIter iter;
|
|
|
|
|
|
|
|
struct ofono_phone_number number;
|
|
|
|
int index = 0;
|
|
|
|
int cssi = -1;
|
|
|
|
int cssu = -1;
|
|
|
|
uint8_t call_id;
|
|
|
|
|
|
|
|
if (ovc == NULL || g_isi_msg_id(msg) != CALL_GSM_NOTIFICATION_IND ||
|
|
|
|
!g_isi_msg_data_get_byte(msg, 0, &call_id) ||
|
|
|
|
(call_id & 7) == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
DBG("Received CallServer notification for call: 0x%02X", call_id);
|
|
|
|
|
|
|
|
for (g_isi_sb_iter_init(&iter, msg, 2);
|
|
|
|
g_isi_sb_iter_is_valid(&iter);
|
|
|
|
g_isi_sb_iter_next(&iter)) {
|
|
|
|
|
|
|
|
switch (g_isi_sb_iter_get_id(&iter)) {
|
|
|
|
case CALL_GSM_NOTIFY:
|
|
|
|
if (!decode_notify(&iter))
|
|
|
|
return;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CALL_GSM_SS_CODE:
|
|
|
|
if (!decode_ss_code(&iter, &cssi, &cssu))
|
|
|
|
return;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CALL_GSM_SS_STATUS:
|
|
|
|
if (!decode_ss_status(&iter))
|
|
|
|
return;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CALL_GSM_SS_NOTIFY:
|
|
|
|
if (!decode_ss_notify(&iter, &cssi, &cssu))
|
|
|
|
return;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CALL_GSM_SS_NOTIFY_INDICATOR:
|
|
|
|
if (!decode_ss_notify_indicator(&iter, &cssi))
|
|
|
|
return;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CALL_GSM_SS_HOLD_INDICATOR:
|
|
|
|
if (!decode_ss_hold_indicator(&iter, &cssu))
|
|
|
|
return;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CALL_GSM_SS_ECT_INDICATOR:
|
|
|
|
if (!decode_ss_ect_indicator(&iter, &cssu))
|
|
|
|
return;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CALL_GSM_REMOTE_ADDRESS:
|
|
|
|
if (!decode_remote_address(&iter, &number, &index))
|
|
|
|
return;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CALL_GSM_REMOTE_SUBADDRESS:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CALL_GSM_CUG_INFO:
|
|
|
|
if (!decode_cug_info(&iter, &index, &cssu))
|
|
|
|
return;
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CALL_ORIGIN_INFO:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CALL_GSM_ALERTING_PATTERN:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CALL_ALERTING_INFO:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cssi != -1)
|
|
|
|
ofono_voicecall_ssn_mo_notify(ovc, call_id & 7, cssi, index);
|
|
|
|
|
|
|
|
if (cssu != -1)
|
|
|
|
ofono_voicecall_ssn_mt_notify(ovc, call_id & 7, cssu, index,
|
|
|
|
&number);
|
|
|
|
}
|
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
static void isi_call_answer_resp(const GIsiMessage *msg, void *data)
|
2009-10-09 14:45:37 +00:00
|
|
|
{
|
2010-11-14 17:31:06 +00:00
|
|
|
struct isi_call_req_ctx *irc = data;
|
|
|
|
uint8_t call_id;
|
2009-10-09 14:45:37 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
if (!check_response_status(msg, CALL_ANSWER_RESP) ||
|
|
|
|
!g_isi_msg_data_get_byte(msg, 0, &call_id) ||
|
|
|
|
call_id == CALL_ID_NONE) {
|
|
|
|
isi_ctx_return_failure(irc);
|
|
|
|
return;
|
|
|
|
}
|
2009-10-09 14:45:37 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
isi_ctx_return_success(irc);
|
2009-10-09 14:45:37 +00:00
|
|
|
}
|
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
static struct isi_call_req_ctx *isi_call_answer_req(struct ofono_voicecall *ovc,
|
|
|
|
uint8_t call_id,
|
|
|
|
ofono_voicecall_cb_t cb,
|
|
|
|
void *data)
|
2009-10-09 14:45:37 +00:00
|
|
|
{
|
2010-11-14 17:31:06 +00:00
|
|
|
const uint8_t req[] = {
|
|
|
|
CALL_ANSWER_REQ,
|
|
|
|
call_id,
|
|
|
|
0
|
2009-10-09 14:45:37 +00:00
|
|
|
};
|
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
return isi_call_req(ovc, req, sizeof(req), isi_call_answer_resp,
|
|
|
|
cb, data);
|
2009-10-09 14:45:37 +00:00
|
|
|
}
|
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
static void isi_call_release_resp(const GIsiMessage *msg, void *data)
|
2009-10-09 14:45:37 +00:00
|
|
|
{
|
2010-11-14 17:31:06 +00:00
|
|
|
struct isi_call_req_ctx *irc = data;
|
|
|
|
GIsiSubBlockIter iter;
|
|
|
|
uint8_t cause_type;
|
|
|
|
uint8_t cause;
|
2009-10-09 14:45:37 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
if (!check_response_status(msg, CALL_RELEASE_RESP))
|
|
|
|
goto error;
|
2009-10-09 14:45:37 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
for (g_isi_sb_iter_init(&iter, msg, 2);
|
|
|
|
g_isi_sb_iter_is_valid(&iter);
|
|
|
|
g_isi_sb_iter_next(&iter)) {
|
|
|
|
|
|
|
|
if (g_isi_sb_iter_get_id(&iter) != CALL_CAUSE)
|
2009-10-09 14:45:37 +00:00
|
|
|
continue;
|
2010-11-14 17:31:06 +00:00
|
|
|
|
|
|
|
if (!g_isi_sb_iter_get_byte(&iter, &cause_type, 2) ||
|
|
|
|
!g_isi_sb_iter_get_byte(&iter, &cause, 3))
|
|
|
|
goto error;
|
2009-10-09 14:45:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ((cause_type == CALL_CAUSE_TYPE_SERVER ||
|
2010-05-27 19:03:24 +00:00
|
|
|
cause_type == CALL_CAUSE_TYPE_CLIENT) &&
|
|
|
|
(cause == CALL_CAUSE_RELEASE_BY_USER ||
|
2010-11-14 17:31:06 +00:00
|
|
|
cause == CALL_CAUSE_BUSY_USER_REQUEST)) {
|
|
|
|
isi_ctx_return_success(irc);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
error:
|
|
|
|
isi_ctx_return_failure(irc);
|
2009-10-09 14:45:37 +00:00
|
|
|
}
|
|
|
|
|
2011-02-23 11:43:21 +00:00
|
|
|
static struct isi_call_req_ctx *isi_call_release_req(
|
|
|
|
struct ofono_voicecall *ovc,
|
2010-11-14 17:31:06 +00:00
|
|
|
uint8_t call_id,
|
|
|
|
enum call_cause_type cause_type,
|
|
|
|
uint8_t cause,
|
|
|
|
ofono_voicecall_cb_t cb,
|
|
|
|
void *data)
|
2009-10-09 14:45:37 +00:00
|
|
|
{
|
2010-11-14 17:31:06 +00:00
|
|
|
const uint8_t req[] = {
|
|
|
|
CALL_RELEASE_REQ,
|
|
|
|
call_id,
|
|
|
|
1, /* Sub-block count */
|
|
|
|
CALL_CAUSE,
|
|
|
|
4, /* Sub-block length */
|
|
|
|
cause_type,
|
|
|
|
cause,
|
2009-10-09 14:45:37 +00:00
|
|
|
};
|
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
return isi_call_req(ovc, req, sizeof(req), isi_call_release_resp,
|
|
|
|
cb, data);
|
2009-10-09 14:45:37 +00:00
|
|
|
}
|
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
static void isi_call_status_resp(const GIsiMessage *msg, void *data)
|
2009-10-09 14:45:37 +00:00
|
|
|
{
|
2010-11-14 17:31:06 +00:00
|
|
|
struct isi_call_req_ctx *irc = data;
|
2009-10-09 14:45:37 +00:00
|
|
|
struct ofono_voicecall *ovc = irc->ovc;
|
|
|
|
struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
|
2010-11-14 17:31:06 +00:00
|
|
|
GIsiSubBlockIter iter;
|
2009-10-09 14:45:37 +00:00
|
|
|
struct isi_call *call = NULL;
|
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
if (!check_response_status(msg, CALL_STATUS_RESP)) {
|
|
|
|
isi_ctx_return_failure(irc);
|
|
|
|
return;
|
|
|
|
}
|
2009-10-09 14:45:37 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
for (g_isi_sb_iter_init(&iter, msg, 2);
|
|
|
|
g_isi_sb_iter_is_valid(&iter);
|
|
|
|
g_isi_sb_iter_next(&iter)) {
|
2009-10-09 14:45:37 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
switch (g_isi_sb_iter_get_id(&iter)) {
|
2009-10-09 14:45:37 +00:00
|
|
|
case CALL_STATUS_INFO:
|
2010-11-14 17:31:06 +00:00
|
|
|
call = isi_call_status_info_sb_proc(ivc, &iter);
|
2009-10-09 14:45:37 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case CALL_ADDR_AND_STATUS_INFO:
|
2011-02-23 11:43:21 +00:00
|
|
|
call = isi_call_addr_and_status_info_sb_proc(ivc,
|
|
|
|
&iter);
|
2009-10-09 14:45:37 +00:00
|
|
|
if (call)
|
|
|
|
isi_call_notify(ovc, call);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case CALL_CAUSE:
|
2010-11-14 17:31:06 +00:00
|
|
|
|
2009-10-09 14:45:37 +00:00
|
|
|
if (call)
|
2010-11-14 17:31:06 +00:00
|
|
|
isi_call_cause_sb_proc(ivc, call, &iter);
|
2009-10-09 14:45:37 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
isi_ctx_return_success(irc);
|
2009-10-09 14:45:37 +00:00
|
|
|
}
|
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
static struct isi_call_req_ctx *isi_call_status_req(struct ofono_voicecall *ovc,
|
|
|
|
uint8_t call_id,
|
|
|
|
uint8_t mode,
|
|
|
|
ofono_voicecall_cb_t cb,
|
|
|
|
void *data)
|
2009-10-09 14:45:37 +00:00
|
|
|
{
|
2010-11-14 17:31:06 +00:00
|
|
|
const uint8_t req[] = {
|
|
|
|
CALL_STATUS_REQ,
|
|
|
|
call_id,
|
|
|
|
1, /* Sub-block count */
|
|
|
|
CALL_STATUS_MODE,
|
|
|
|
4, /* Sub-block length */
|
|
|
|
mode, 0,
|
2009-10-09 14:45:37 +00:00
|
|
|
};
|
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
return isi_call_req(ovc, req, sizeof(req), isi_call_status_resp,
|
|
|
|
cb, data);
|
2009-10-09 14:45:37 +00:00
|
|
|
}
|
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
static void isi_call_control_resp(const GIsiMessage *msg, void *data)
|
|
|
|
{
|
|
|
|
struct isi_call_req_ctx *irc = data;
|
|
|
|
GIsiSubBlockIter iter;
|
|
|
|
uint8_t cause = CALL_CAUSE_NO_CAUSE;
|
|
|
|
uint8_t cause_type = 0;
|
|
|
|
|
|
|
|
if (!check_response_status(msg, CALL_CONTROL_RESP))
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
for (g_isi_sb_iter_init(&iter, msg, 2);
|
|
|
|
g_isi_sb_iter_is_valid(&iter);
|
|
|
|
g_isi_sb_iter_next(&iter)) {
|
|
|
|
|
|
|
|
if (g_isi_sb_iter_get_id(&iter) != CALL_CAUSE)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (!g_isi_sb_iter_get_byte(&iter, &cause_type, 2) ||
|
|
|
|
!g_isi_sb_iter_get_byte(&iter, &cause, 3))
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cause == CALL_CAUSE_NO_CAUSE) {
|
2011-01-12 15:16:28 +00:00
|
|
|
isi_ctx_return_success(irc);
|
2010-11-14 17:31:06 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
error:
|
|
|
|
isi_ctx_return_failure(irc);
|
|
|
|
}
|
|
|
|
|
2011-02-23 11:43:21 +00:00
|
|
|
static struct isi_call_req_ctx *isi_call_control_req(
|
|
|
|
struct ofono_voicecall *ovc,
|
|
|
|
uint8_t call_id,
|
|
|
|
enum call_operation op,
|
|
|
|
uint8_t info,
|
|
|
|
ofono_voicecall_cb_t cb,
|
|
|
|
void *data)
|
2010-11-14 17:31:06 +00:00
|
|
|
{
|
|
|
|
const uint8_t req[] = {
|
|
|
|
CALL_CONTROL_REQ,
|
|
|
|
call_id,
|
|
|
|
1, /* Sub-block count */
|
|
|
|
CALL_OPERATION,
|
|
|
|
4, /* Sub-block length */
|
|
|
|
op, info,
|
|
|
|
};
|
|
|
|
|
|
|
|
return isi_call_req(ovc, req, sizeof(req), isi_call_control_resp,
|
|
|
|
cb, data);
|
|
|
|
}
|
|
|
|
|
2011-02-23 11:43:21 +00:00
|
|
|
static struct isi_call_req_ctx *isi_call_deflect_req(
|
|
|
|
struct ofono_voicecall *ovc,
|
|
|
|
uint8_t call_id,
|
|
|
|
uint8_t address_type,
|
|
|
|
const char address[21],
|
|
|
|
ofono_voicecall_cb_t cb,
|
|
|
|
void *data)
|
2009-10-09 14:45:37 +00:00
|
|
|
{
|
|
|
|
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] = {
|
2010-11-14 17:31:06 +00:00
|
|
|
CALL_CONTROL_REQ,
|
|
|
|
call_id,
|
|
|
|
2, /* Sub-block count */
|
|
|
|
CALL_OPERATION,
|
|
|
|
4, /* Sub-block length */
|
|
|
|
CALL_GSM_OP_DEFLECT, 0,
|
|
|
|
CALL_GSM_DEFLECTION_ADDRESS,
|
2011-02-01 17:32:50 +00:00
|
|
|
sub_len, /* Sub-block length */
|
2009-10-09 14:45:37 +00:00
|
|
|
address_type & 0x7F,
|
2010-11-14 17:31:06 +00:00
|
|
|
0x7, /* Default presentation */
|
|
|
|
0, /* Filler */
|
2009-10-09 14:45:37 +00:00
|
|
|
addr_len,
|
|
|
|
};
|
|
|
|
|
|
|
|
if (addr_len > 20) {
|
|
|
|
CALLBACK_WITH_FAILURE(cb, data);
|
2010-11-14 17:31:06 +00:00
|
|
|
return NULL;
|
2009-10-09 14:45:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2011-02-23 11:43:21 +00:00
|
|
|
static void isi_call_control_ind_cb(const GIsiMessage *msg, void *data)
|
|
|
|
{
|
|
|
|
struct ofono_voicecall *ovc = data;
|
|
|
|
struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
|
|
|
|
GIsiSubBlockIter iter;
|
|
|
|
uint8_t cause_type = 0, cause = 0;
|
|
|
|
|
|
|
|
if (ivc == NULL || g_isi_msg_id(msg) != CALL_CONTROL_IND)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (g_isi_sb_iter_init(&iter, msg, 2);
|
|
|
|
g_isi_sb_iter_is_valid(&iter);
|
|
|
|
g_isi_sb_iter_next(&iter)) {
|
|
|
|
|
|
|
|
if (g_isi_sb_iter_get_id(&iter) != CALL_CAUSE)
|
|
|
|
continue;
|
|
|
|
if (!g_isi_sb_iter_get_byte(&iter, &cause_type, 2) ||
|
|
|
|
!g_isi_sb_iter_get_byte(&iter, &cause, 3))
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ivc->control_req_irc) {
|
|
|
|
if (!cause)
|
|
|
|
isi_ctx_return_success(ivc->control_req_irc);
|
|
|
|
else
|
|
|
|
isi_ctx_return_failure(ivc->control_req_irc);
|
|
|
|
|
|
|
|
ivc->control_req_irc = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
static void isi_call_dtmf_send_resp(const GIsiMessage *msg, void *data)
|
2009-10-09 14:45:37 +00:00
|
|
|
{
|
2010-11-14 17:31:06 +00:00
|
|
|
struct isi_call_req_ctx *irc = data;
|
|
|
|
GIsiSubBlockIter iter;
|
|
|
|
uint8_t cause_type;
|
2011-01-20 14:13:01 +00:00
|
|
|
uint8_t cause = CALL_CAUSE_NO_CAUSE;
|
2009-10-09 14:45:37 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
if (!check_response_status(msg, CALL_DTMF_SEND_RESP))
|
|
|
|
goto error;
|
2009-10-09 14:45:37 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
for (g_isi_sb_iter_init(&iter, msg, 2);
|
|
|
|
g_isi_sb_iter_is_valid(&iter);
|
|
|
|
g_isi_sb_iter_next(&iter)) {
|
|
|
|
|
|
|
|
if (g_isi_sb_iter_get_id(&iter) != CALL_CAUSE)
|
2009-10-09 14:45:37 +00:00
|
|
|
continue;
|
2010-11-14 17:31:06 +00:00
|
|
|
|
|
|
|
if (!g_isi_sb_iter_get_byte(&iter, &cause_type, 2) ||
|
|
|
|
!g_isi_sb_iter_get_byte(&iter, &cause, 3))
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cause == CALL_CAUSE_NO_CAUSE) {
|
|
|
|
isi_ctx_return_success(irc);
|
|
|
|
return;
|
2009-10-09 14:45:37 +00:00
|
|
|
}
|
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
error:
|
|
|
|
isi_ctx_return_failure(irc);
|
2009-10-09 14:45:37 +00:00
|
|
|
}
|
|
|
|
|
2011-02-23 11:43:21 +00:00
|
|
|
static struct isi_call_req_ctx *isi_call_dtmf_send_req(
|
|
|
|
struct ofono_voicecall *ovc,
|
|
|
|
uint8_t call_id,
|
|
|
|
const char *string,
|
|
|
|
ofono_voicecall_cb_t cb,
|
|
|
|
void *data)
|
2009-10-09 14:45:37 +00:00
|
|
|
{
|
|
|
|
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);
|
2010-05-27 16:12:46 +00:00
|
|
|
return FALSE;
|
2009-10-09 14:45:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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 void isi_dial(struct ofono_voicecall *ovc,
|
2011-03-03 07:39:29 +00:00
|
|
|
const struct ofono_phone_number *number,
|
2011-01-26 22:17:52 +00:00
|
|
|
enum ofono_clir_option clir, ofono_voicecall_cb_t cb,
|
|
|
|
void *data)
|
2009-10-09 14:45:37 +00:00
|
|
|
{
|
2011-04-12 16:47:27 +00:00
|
|
|
struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
|
|
|
|
gboolean have_pn_call = g_isi_client_resource(ivc->client) == PN_CALL;
|
2011-02-23 11:43:21 +00:00
|
|
|
unsigned char presentation;
|
2009-10-09 14:45:37 +00:00
|
|
|
|
2010-05-27 16:12:47 +00:00
|
|
|
switch (clir) {
|
2009-10-09 14:45:37 +00:00
|
|
|
case OFONO_CLIR_OPTION_INVOCATION:
|
|
|
|
presentation = CALL_PRESENTATION_RESTRICTED;
|
|
|
|
break;
|
|
|
|
case OFONO_CLIR_OPTION_SUPPRESSION:
|
|
|
|
presentation = CALL_PRESENTATION_ALLOWED;
|
|
|
|
break;
|
2011-02-23 11:43:21 +00:00
|
|
|
case OFONO_CLIR_OPTION_DEFAULT:
|
|
|
|
default:
|
2011-04-12 16:47:27 +00:00
|
|
|
presentation = have_pn_call ? CALL_GSM_PRESENTATION_DEFAULT :
|
|
|
|
CALL_MODEM_PROP_PRESENT_DEFAULT;
|
2011-03-03 07:39:29 +00:00
|
|
|
}
|
|
|
|
|
2011-04-12 16:47:27 +00:00
|
|
|
if (have_pn_call)
|
|
|
|
isi_call_create_req(ovc, presentation, number->type,
|
2011-03-03 07:39:29 +00:00
|
|
|
number->number, cb, data);
|
2011-04-12 16:47:27 +00:00
|
|
|
else
|
|
|
|
isi_modem_call_create_req(ovc, presentation, number->type,
|
|
|
|
number->number, cb, data);
|
2011-03-03 07:39:29 +00:00
|
|
|
}
|
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
static void isi_answer(struct ofono_voicecall *ovc, ofono_voicecall_cb_t cb,
|
|
|
|
void *data)
|
2009-10-09 14:45:37 +00:00
|
|
|
{
|
2012-09-10 12:16:07 +00:00
|
|
|
struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
|
|
|
|
int id;
|
|
|
|
|
2012-09-12 04:30:04 +00:00
|
|
|
for (id = 1; id <= 7; id++)
|
2012-09-10 12:16:07 +00:00
|
|
|
if (ivc->calls[id].status == CALL_STATUS_MT_ALERTING)
|
|
|
|
goto answer_by_id;
|
|
|
|
|
|
|
|
id = CALL_ID_ALL;
|
|
|
|
|
|
|
|
answer_by_id:
|
|
|
|
isi_call_answer_req(ovc, id, cb, data);
|
2009-10-09 14:45:37 +00:00
|
|
|
}
|
|
|
|
|
2010-10-20 17:08:29 +00:00
|
|
|
static void isi_hangup_current(struct ofono_voicecall *ovc,
|
2010-11-14 17:31:06 +00:00
|
|
|
ofono_voicecall_cb_t cb, void *data)
|
2009-08-24 12:47:17 +00:00
|
|
|
{
|
2010-10-20 17:08:29 +00:00
|
|
|
/*
|
|
|
|
* Hangup call(s) that are not held or waiting:
|
|
|
|
* active calls or calls in progress.
|
|
|
|
*/
|
|
|
|
struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
|
2010-11-14 17:31:06 +00:00
|
|
|
int id = 0;
|
2011-01-21 08:24:30 +00:00
|
|
|
uint8_t cause = CALL_CAUSE_RELEASE_BY_USER;
|
2010-10-20 17:08:29 +00:00
|
|
|
|
|
|
|
for (id = 1; id <= 7; id++) {
|
|
|
|
if (ivc->calls[id].call_id & CALL_ID_WAITING)
|
|
|
|
continue;
|
|
|
|
if (ivc->calls[id].call_id & CALL_ID_HOLD)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
switch (ivc->calls[id].status) {
|
|
|
|
case CALL_STATUS_CREATE:
|
|
|
|
case CALL_STATUS_COMING:
|
|
|
|
case CALL_STATUS_MO_ALERTING:
|
2010-11-14 17:31:06 +00:00
|
|
|
case CALL_STATUS_ANSWERED:
|
2011-04-05 12:02:59 +00:00
|
|
|
case CALL_STATUS_HOLD_INITIATED:
|
2010-10-20 17:08:29 +00:00
|
|
|
goto release_by_id;
|
2011-01-21 08:24:30 +00:00
|
|
|
case CALL_STATUS_MT_ALERTING:
|
|
|
|
cause = CALL_CAUSE_BUSY_USER_REQUEST;
|
|
|
|
goto release_by_id;
|
|
|
|
case CALL_STATUS_PROCEEDING:
|
|
|
|
if (ivc->calls[id].mode_info & CALL_MODE_ORIGINATOR)
|
|
|
|
cause = CALL_CAUSE_BUSY_USER_REQUEST;
|
|
|
|
goto release_by_id;
|
2010-10-20 17:08:29 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
id = CALL_ID_ACTIVE;
|
|
|
|
|
|
|
|
release_by_id:
|
2011-01-21 08:24:30 +00:00
|
|
|
isi_call_release_req(ovc, id, CALL_CAUSE_TYPE_CLIENT, cause, cb, data);
|
2009-10-09 14:45:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void isi_release_all_held(struct ofono_voicecall *ovc,
|
2010-11-14 17:31:06 +00:00
|
|
|
ofono_voicecall_cb_t cb, void *data)
|
2009-08-24 12:47:17 +00:00
|
|
|
{
|
2010-11-14 17:31:06 +00:00
|
|
|
isi_call_release_req(ovc, CALL_ID_HOLD, CALL_CAUSE_TYPE_CLIENT,
|
2010-05-27 19:03:24 +00:00
|
|
|
CALL_CAUSE_RELEASE_BY_USER, cb, data);
|
2009-08-24 12:47:17 +00:00
|
|
|
}
|
|
|
|
|
2009-10-09 14:45:37 +00:00
|
|
|
static void isi_set_udub(struct ofono_voicecall *ovc,
|
2010-05-27 19:03:24 +00:00
|
|
|
ofono_voicecall_cb_t cb, void *data)
|
2009-08-24 12:47:17 +00:00
|
|
|
{
|
2010-10-20 17:08:29 +00:00
|
|
|
/* Release waiting calls */
|
|
|
|
isi_call_release_req(ovc, CALL_ID_WAITING,
|
2010-11-14 17:31:06 +00:00
|
|
|
CALL_CAUSE_TYPE_CLIENT,
|
2010-12-03 08:30:49 +00:00
|
|
|
CALL_CAUSE_BUSY_USER_REQUEST, cb, data);
|
2009-10-09 14:45:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void isi_retrieve(struct ofono_voicecall *ovc,
|
2010-05-27 19:03:24 +00:00
|
|
|
ofono_voicecall_cb_t cb, void *data)
|
2009-10-09 14:45:37 +00:00
|
|
|
{
|
2010-05-27 19:03:24 +00:00
|
|
|
isi_call_control_req(ovc, CALL_ID_HOLD, CALL_OP_RETRIEVE, 0, cb, data);
|
2009-10-09 14:45:37 +00:00
|
|
|
}
|
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
static void isi_wait_and_answer(struct isi_call_req_ctx *irc, int event)
|
2010-12-03 08:30:50 +00:00
|
|
|
{
|
2011-01-03 15:45:56 +00:00
|
|
|
DBG("irc=%p event=%u", (void *) irc, event);
|
2010-12-03 08:30:50 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
if (event != CALL_STATUS_TERMINATED)
|
2010-12-03 08:30:50 +00:00
|
|
|
return;
|
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
isi_answer(irc->ovc, irc->cb, irc->data);
|
|
|
|
isi_ctx_free(irc);
|
|
|
|
}
|
2010-12-03 08:30:50 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
static void isi_wait_and_retrieve(struct isi_call_req_ctx *irc, int event)
|
|
|
|
{
|
2011-01-03 15:45:56 +00:00
|
|
|
DBG("irc=%p event=%u", (void *) irc, event);
|
2010-11-14 17:31:06 +00:00
|
|
|
|
|
|
|
if (event != CALL_STATUS_TERMINATED)
|
|
|
|
return;
|
|
|
|
|
|
|
|
isi_retrieve(irc->ovc, irc->cb, irc->data);
|
|
|
|
isi_ctx_free(irc);
|
2010-12-03 08:30:50 +00:00
|
|
|
}
|
2009-10-09 14:45:37 +00:00
|
|
|
|
|
|
|
static void isi_release_all_active(struct ofono_voicecall *ovc,
|
2010-05-27 19:03:24 +00:00
|
|
|
ofono_voicecall_cb_t cb, void *data)
|
2009-10-09 14:45:37 +00:00
|
|
|
{
|
|
|
|
struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
|
2010-11-14 17:31:06 +00:00
|
|
|
struct isi_call_req_ctx *irc;
|
|
|
|
int id = 0;
|
|
|
|
int waiting = 0;
|
2010-12-03 08:30:50 +00:00
|
|
|
int active = 0;
|
2010-11-14 17:31:06 +00:00
|
|
|
int hold = 0;
|
2009-10-09 14:45:37 +00:00
|
|
|
|
|
|
|
for (id = 1; id <= 7; id++) {
|
2010-11-14 17:31:06 +00:00
|
|
|
|
2009-10-09 14:45:37 +00:00
|
|
|
if (ivc->calls[id].call_id & CALL_ID_WAITING)
|
2010-11-14 17:31:06 +00:00
|
|
|
waiting++;
|
|
|
|
|
|
|
|
if (ivc->calls[id].call_id & CALL_ID_HOLD)
|
|
|
|
hold++;
|
2010-12-03 08:30:50 +00:00
|
|
|
|
2009-10-09 14:45:37 +00:00
|
|
|
if (ivc->calls[id].call_id & CALL_ID_ACTIVE)
|
|
|
|
active++;
|
|
|
|
}
|
|
|
|
|
2010-12-03 08:30:50 +00:00
|
|
|
if (!active) {
|
2009-10-09 14:45:37 +00:00
|
|
|
CALLBACK_WITH_FAILURE(cb, data);
|
2010-12-03 08:30:50 +00:00
|
|
|
return;
|
2009-10-09 14:45:37 +00:00
|
|
|
}
|
2009-08-24 12:47:17 +00:00
|
|
|
|
2010-12-03 08:30:50 +00:00
|
|
|
irc = isi_call_release_req(ovc, CALL_ID_ACTIVE,
|
2010-11-14 17:31:06 +00:00
|
|
|
CALL_CAUSE_TYPE_CLIENT,
|
2010-12-03 08:30:50 +00:00
|
|
|
CALL_CAUSE_RELEASE_BY_USER,
|
|
|
|
cb, data);
|
2010-12-01 13:46:57 +00:00
|
|
|
if (irc == NULL)
|
2010-12-03 08:30:50 +00:00
|
|
|
return;
|
2010-12-03 08:30:48 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
if (waiting)
|
|
|
|
isi_ctx_queue(irc, isi_wait_and_answer);
|
|
|
|
else if (hold)
|
|
|
|
isi_ctx_queue(irc, isi_wait_and_retrieve);
|
2009-10-09 14:45:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void isi_hold_all_active(struct ofono_voicecall *ovc,
|
2010-05-27 19:03:24 +00:00
|
|
|
ofono_voicecall_cb_t cb, void *data)
|
2009-08-24 12:47:17 +00:00
|
|
|
{
|
2009-10-09 14:45:37 +00:00
|
|
|
struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
|
2010-11-14 17:31:06 +00:00
|
|
|
int id = 0;
|
|
|
|
int op = 0;
|
2010-12-03 08:30:50 +00:00
|
|
|
int waiting = 0;
|
|
|
|
int active = 0;
|
|
|
|
int hold = 0;
|
2009-10-09 14:45:37 +00:00
|
|
|
|
|
|
|
for (id = 1; id <= 7; id++) {
|
2010-11-14 17:31:06 +00:00
|
|
|
|
2009-10-09 14:45:37 +00:00
|
|
|
if (ivc->calls[id].call_id & CALL_ID_WAITING)
|
|
|
|
waiting++;
|
2010-11-14 17:31:06 +00:00
|
|
|
|
|
|
|
if (ivc->calls[id].call_id & CALL_ID_HOLD)
|
2009-10-09 14:45:37 +00:00
|
|
|
hold++;
|
2010-11-14 17:31:06 +00:00
|
|
|
|
|
|
|
if (ivc->calls[id].call_id & CALL_ID_ACTIVE)
|
2009-10-09 14:45:37 +00:00
|
|
|
active++;
|
|
|
|
}
|
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
if (!waiting && !hold && !active) {
|
|
|
|
CALLBACK_WITH_FAILURE(cb, data);
|
2010-12-03 08:30:50 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
if (waiting) {
|
|
|
|
isi_call_answer_req(ovc, CALL_ID_WAITING, cb, data);
|
|
|
|
|
|
|
|
} else if (hold) {
|
|
|
|
|
|
|
|
if (active) {
|
2010-05-27 16:12:47 +00:00
|
|
|
op = CALL_OP_SWAP;
|
2010-11-14 17:31:06 +00:00
|
|
|
id = CALL_ID_ACTIVE;
|
|
|
|
} else {
|
|
|
|
op = CALL_OP_RETRIEVE;
|
|
|
|
id = CALL_ID_HOLD;
|
|
|
|
}
|
|
|
|
isi_call_control_req(ovc, id, op, 0, cb, data);
|
2010-12-03 08:30:50 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
} else if (active) {
|
|
|
|
id = CALL_ID_ACTIVE;
|
|
|
|
op = CALL_OP_HOLD;
|
2010-12-03 08:30:50 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
isi_call_control_req(ovc, id, op, 0, cb, data);
|
|
|
|
}
|
2009-08-24 12:47:17 +00:00
|
|
|
}
|
|
|
|
|
2009-10-09 14:45:37 +00:00
|
|
|
static void isi_release_specific(struct ofono_voicecall *ovc, int id,
|
2010-05-27 19:03:24 +00:00
|
|
|
ofono_voicecall_cb_t cb, void *data)
|
2009-08-24 12:47:17 +00:00
|
|
|
{
|
2009-10-09 14:45:37 +00:00
|
|
|
struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
|
2010-11-14 17:31:06 +00:00
|
|
|
const struct isi_call *status;
|
|
|
|
uint8_t cause;
|
2009-10-09 14:45:37 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
if (id < 1 || id > 7) {
|
|
|
|
CALLBACK_WITH_FAILURE(cb, data);
|
|
|
|
return;
|
|
|
|
}
|
2009-10-09 14:45:37 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
status = &ivc->calls[id];
|
|
|
|
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))
|
2009-10-09 14:45:37 +00:00
|
|
|
cause = CALL_CAUSE_BUSY_USER_REQUEST;
|
2016-04-18 11:45:34 +00:00
|
|
|
break;
|
2010-11-14 17:31:06 +00:00
|
|
|
}
|
2009-10-09 14:45:37 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
isi_call_release_req(ovc, id, CALL_CAUSE_TYPE_CLIENT, cause, cb, data);
|
2009-10-09 14:45:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void isi_private_chat(struct ofono_voicecall *ovc, int id,
|
2010-05-27 19:03:24 +00:00
|
|
|
ofono_voicecall_cb_t cb, void *data)
|
2009-10-09 14:45:37 +00:00
|
|
|
{
|
2010-11-14 17:31:06 +00:00
|
|
|
if (id < 1 || id > 7) {
|
2009-10-09 14:45:37 +00:00
|
|
|
CALLBACK_WITH_FAILURE(cb, data);
|
2010-11-14 17:31:06 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
isi_call_control_req(ovc, id, CALL_OP_CONFERENCE_SPLIT, 0, cb, data);
|
2009-10-09 14:45:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void isi_create_multiparty(struct ofono_voicecall *ovc,
|
2010-05-27 19:03:24 +00:00
|
|
|
ofono_voicecall_cb_t cb, void *data)
|
2009-10-09 14:45:37 +00:00
|
|
|
{
|
2010-05-27 19:03:24 +00:00
|
|
|
isi_call_control_req(ovc, CALL_ID_ALL, CALL_OP_CONFERENCE_BUILD, 0,
|
|
|
|
cb, data);
|
2009-08-24 12:47:17 +00:00
|
|
|
}
|
|
|
|
|
2009-10-09 14:45:37 +00:00
|
|
|
static void isi_transfer(struct ofono_voicecall *ovc,
|
2010-11-14 17:31:06 +00:00
|
|
|
ofono_voicecall_cb_t cb, void *data)
|
2009-08-24 12:47:17 +00:00
|
|
|
{
|
2009-10-09 14:45:37 +00:00
|
|
|
struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
|
|
|
|
uint8_t id;
|
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
for (id = 1; id <= 7; id++) {
|
|
|
|
|
2009-10-09 14:45:37 +00:00
|
|
|
if (ivc->calls[id].status == CALL_STATUS_MO_ALERTING)
|
|
|
|
break;
|
2010-11-14 17:31:06 +00:00
|
|
|
}
|
|
|
|
|
2009-10-09 14:45:37 +00:00
|
|
|
if (id > 7)
|
|
|
|
id = CALL_ID_ACTIVE;
|
|
|
|
|
2010-05-27 19:03:24 +00:00
|
|
|
isi_call_control_req(ovc, id, CALL_GSM_OP_TRANSFER, 0, cb, data);
|
2009-08-24 12:47:17 +00:00
|
|
|
}
|
|
|
|
|
2009-10-09 14:45:37 +00:00
|
|
|
static void isi_deflect(struct ofono_voicecall *ovc,
|
2009-08-24 12:47:17 +00:00
|
|
|
const struct ofono_phone_number *ph,
|
|
|
|
ofono_voicecall_cb_t cb, void *data)
|
|
|
|
{
|
2010-11-14 17:31:06 +00:00
|
|
|
isi_call_deflect_req(ovc, CALL_ID_WAITING, ph->type, ph->number,
|
|
|
|
cb, data);
|
2009-08-24 12:47:17 +00:00
|
|
|
}
|
|
|
|
|
2009-10-09 14:45:37 +00:00
|
|
|
static void isi_swap_without_accept(struct ofono_voicecall *ovc,
|
2010-11-14 17:31:06 +00:00
|
|
|
ofono_voicecall_cb_t cb, void *data)
|
2009-08-24 12:47:17 +00:00
|
|
|
{
|
2009-10-09 14:45:37 +00:00
|
|
|
struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
|
2010-11-14 17:31:06 +00:00
|
|
|
int id = 0;
|
|
|
|
int op = 0;
|
|
|
|
int active = 0;
|
|
|
|
int hold = 0;
|
2009-10-09 14:45:37 +00:00
|
|
|
|
|
|
|
for (id = 1; id <= 7; id++) {
|
2010-11-14 17:31:06 +00:00
|
|
|
|
2009-10-09 14:45:37 +00:00
|
|
|
if (ivc->calls[id].call_id & CALL_ID_HOLD)
|
|
|
|
hold++;
|
2010-11-14 17:31:06 +00:00
|
|
|
|
2009-10-09 14:45:37 +00:00
|
|
|
if (ivc->calls[id].call_id & CALL_ID_ACTIVE)
|
|
|
|
active++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hold && active) {
|
2010-11-14 17:31:06 +00:00
|
|
|
id = CALL_ID_ACTIVE;
|
|
|
|
op = CALL_OP_SWAP;
|
2010-05-27 16:12:47 +00:00
|
|
|
} else if (active) {
|
2010-11-14 17:31:06 +00:00
|
|
|
id = CALL_ID_ACTIVE;
|
|
|
|
op = CALL_OP_HOLD;
|
2010-05-27 16:12:47 +00:00
|
|
|
} else if (hold) {
|
2010-11-14 17:31:06 +00:00
|
|
|
id = CALL_ID_HOLD;
|
|
|
|
op = CALL_OP_RETRIEVE;
|
2010-05-27 16:12:47 +00:00
|
|
|
} else {
|
2009-10-09 14:45:37 +00:00
|
|
|
CALLBACK_WITH_FAILURE(cb, data);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
isi_call_control_req(ovc, id, op, 0, cb, data);
|
2009-08-24 12:47:17 +00:00
|
|
|
}
|
|
|
|
|
2009-10-09 14:45:37 +00:00
|
|
|
static void isi_send_tones(struct ofono_voicecall *ovc, const char *tones,
|
2010-05-27 19:03:24 +00:00
|
|
|
ofono_voicecall_cb_t cb, void *data)
|
2009-08-24 12:47:17 +00:00
|
|
|
{
|
2011-12-29 21:30:25 +00:00
|
|
|
isi_call_dtmf_send_req(ovc, CALL_ID_ALL, tones, cb, data);
|
2009-08-24 12:47:17 +00:00
|
|
|
}
|
|
|
|
|
2011-04-12 16:47:27 +00:00
|
|
|
static void subscribe_indications(GIsiClient *cl, void *data)
|
|
|
|
{
|
|
|
|
g_isi_client_ind_subscribe(cl, CALL_STATUS_IND, isi_call_status_ind_cb,
|
|
|
|
data);
|
|
|
|
g_isi_client_ind_subscribe(cl, CALL_CONTROL_IND, isi_call_control_ind_cb,
|
|
|
|
data);
|
|
|
|
g_isi_client_ind_subscribe(cl, CALL_TERMINATED_IND,
|
|
|
|
isi_call_terminated_ind_cb, data);
|
|
|
|
g_isi_client_ind_subscribe(cl, CALL_GSM_NOTIFICATION_IND,
|
|
|
|
notification_ind_cb, data);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pn_call_verify_cb(const GIsiMessage *msg, void *data)
|
|
|
|
{
|
|
|
|
struct ofono_voicecall *ovc = data;
|
|
|
|
struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
|
|
|
|
|
|
|
|
if (g_isi_msg_error(msg) < 0) {
|
|
|
|
DBG("PN_CALL not reachable, removing client");
|
|
|
|
g_isi_client_destroy(ivc->pn_call);
|
|
|
|
ivc->pn_call = NULL;
|
|
|
|
|
|
|
|
if (ivc->pn_modem_call == NULL)
|
|
|
|
ofono_voicecall_remove(ovc);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ISI_RESOURCE_DBG(msg);
|
|
|
|
|
|
|
|
if (ivc == NULL || ivc->client != NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ivc->client = ivc->pn_call;
|
|
|
|
|
|
|
|
subscribe_indications(ivc->client, 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);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void pn_modem_call_verify_cb(const GIsiMessage *msg, void *data)
|
2009-08-24 12:47:17 +00:00
|
|
|
{
|
2010-11-14 17:31:06 +00:00
|
|
|
struct ofono_voicecall *ovc = data;
|
2010-12-03 08:30:47 +00:00
|
|
|
struct isi_voicecall *ivc = ofono_voicecall_get_data(ovc);
|
2009-08-24 12:47:17 +00:00
|
|
|
|
2011-03-31 08:28:53 +00:00
|
|
|
if (g_isi_msg_error(msg) < 0) {
|
2011-04-12 16:47:27 +00:00
|
|
|
DBG("PN_MODEM_CALL not reachable, removing client");
|
|
|
|
g_isi_client_destroy(ivc->pn_modem_call);
|
|
|
|
ivc->pn_modem_call = NULL;
|
|
|
|
|
|
|
|
if (ivc->pn_call == NULL)
|
|
|
|
ofono_voicecall_remove(ovc);
|
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
return;
|
2011-03-31 08:28:53 +00:00
|
|
|
}
|
2010-11-14 17:31:06 +00:00
|
|
|
|
2011-04-11 15:02:35 +00:00
|
|
|
ISI_RESOURCE_DBG(msg);
|
2009-10-09 14:45:37 +00:00
|
|
|
|
2011-04-12 16:47:27 +00:00
|
|
|
if (ivc == NULL || ivc->client != NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ivc->client = ivc->pn_modem_call;
|
2011-02-23 11:43:21 +00:00
|
|
|
|
2011-04-12 16:47:27 +00:00
|
|
|
subscribe_indications(ivc->client, ovc);
|
2011-03-11 19:29:10 +00:00
|
|
|
|
2011-02-23 11:43:21 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2011-04-12 16:47:27 +00:00
|
|
|
static int isi_probe(struct ofono_voicecall *ovc, unsigned int vendor,
|
|
|
|
void *user)
|
2009-10-09 14:45:37 +00:00
|
|
|
{
|
2010-11-14 17:31:06 +00:00
|
|
|
GIsiModem *modem = user;
|
2010-12-03 08:30:47 +00:00
|
|
|
struct isi_voicecall *ivc;
|
|
|
|
int id;
|
2009-10-09 14:45:37 +00:00
|
|
|
|
2010-12-03 08:30:47 +00:00
|
|
|
ivc = g_try_new0(struct isi_voicecall, 1);
|
2010-12-01 13:46:57 +00:00
|
|
|
if (ivc == NULL)
|
2010-12-03 08:30:47 +00:00
|
|
|
return -ENOMEM;
|
2010-01-08 08:58:08 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
for (id = 0; id <= 7; id++)
|
2010-12-03 08:30:47 +00:00
|
|
|
ivc->calls[id].id = id;
|
2009-10-09 14:45:37 +00:00
|
|
|
|
2011-04-12 16:47:27 +00:00
|
|
|
ivc->pn_call = g_isi_client_create(modem, PN_CALL);
|
|
|
|
if (ivc->pn_call == NULL) {
|
|
|
|
g_free(ivc);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
ivc->pn_modem_call = g_isi_client_create(modem, PN_MODEM_CALL);
|
|
|
|
if (ivc->pn_call == NULL) {
|
|
|
|
g_isi_client_destroy(ivc->pn_call);
|
2011-02-23 11:43:21 +00:00
|
|
|
g_free(ivc);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
2009-10-09 14:45:37 +00:00
|
|
|
|
2010-12-03 08:30:47 +00:00
|
|
|
ofono_voicecall_set_data(ovc, ivc);
|
2009-10-09 14:45:37 +00:00
|
|
|
|
2011-04-12 16:47:27 +00:00
|
|
|
g_isi_client_verify(ivc->pn_call, pn_call_verify_cb, ovc, NULL);
|
|
|
|
g_isi_client_verify(ivc->pn_modem_call, pn_modem_call_verify_cb,
|
|
|
|
ovc, NULL);
|
2009-10-09 14:45:37 +00:00
|
|
|
|
2010-12-03 08:30:47 +00:00
|
|
|
return 0;
|
2009-10-09 14:45:37 +00:00
|
|
|
}
|
|
|
|
|
2011-03-03 07:39:29 +00:00
|
|
|
static void isi_remove(struct ofono_voicecall *call)
|
2009-08-24 12:47:17 +00:00
|
|
|
{
|
2009-10-09 14:45:37 +00:00
|
|
|
struct isi_voicecall *data = ofono_voicecall_get_data(call);
|
2009-08-24 12:47:17 +00:00
|
|
|
|
2010-11-14 17:31:06 +00:00
|
|
|
ofono_voicecall_set_data(call, NULL);
|
|
|
|
|
2010-12-01 13:46:57 +00:00
|
|
|
if (data == NULL)
|
2010-10-19 08:28:06 +00:00
|
|
|
return;
|
|
|
|
|
2011-04-12 16:47:27 +00:00
|
|
|
g_isi_client_destroy(data->pn_call);
|
|
|
|
g_isi_client_destroy(data->pn_modem_call);
|
2010-10-19 08:28:06 +00:00
|
|
|
g_free(data);
|
2009-08-24 12:47:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static struct ofono_voicecall_driver driver = {
|
2009-09-02 03:35:14 +00:00
|
|
|
.name = "isimodem",
|
2011-03-03 07:39:29 +00:00
|
|
|
.probe = isi_probe,
|
|
|
|
.remove = isi_remove,
|
2009-08-24 12:47:17 +00:00
|
|
|
.dial = isi_dial,
|
|
|
|
.answer = isi_answer,
|
2010-10-20 17:08:29 +00:00
|
|
|
.hangup_active = isi_hangup_current,
|
2009-08-24 12:47:17 +00:00
|
|
|
.hold_all_active = isi_hold_all_active,
|
|
|
|
.release_all_held = isi_release_all_held,
|
|
|
|
.set_udub = isi_set_udub,
|
|
|
|
.release_all_active = isi_release_all_active,
|
|
|
|
.release_specific = isi_release_specific,
|
|
|
|
.private_chat = isi_private_chat,
|
|
|
|
.create_multiparty = isi_create_multiparty,
|
|
|
|
.transfer = isi_transfer,
|
|
|
|
.deflect = isi_deflect,
|
|
|
|
.swap_without_accept = isi_swap_without_accept,
|
|
|
|
.send_tones = isi_send_tones,
|
|
|
|
};
|
|
|
|
|
2011-01-12 10:52:14 +00:00
|
|
|
void isi_voicecall_init(void)
|
2009-08-24 12:47:17 +00:00
|
|
|
{
|
|
|
|
ofono_voicecall_driver_register(&driver);
|
|
|
|
}
|
|
|
|
|
2011-01-12 10:52:14 +00:00
|
|
|
void isi_voicecall_exit(void)
|
2009-08-24 12:47:17 +00:00
|
|
|
{
|
|
|
|
ofono_voicecall_driver_unregister(&driver);
|
|
|
|
}
|