mirror of git://git.sysmocom.de/ofono
Add initial implementation of AT modem driver
This commit is contained in:
parent
4f54515209
commit
6d486b7fe1
|
@ -4,13 +4,19 @@ builtin_sources =
|
|||
builtin_cflags =
|
||||
|
||||
builtin_modules += atmodem
|
||||
builtin_sources += atmodem/main.c
|
||||
builtin_sources += atmodem/main.c atmodem/at.h \
|
||||
atmodem/session.h atmodem/session.c \
|
||||
atmodem/call-settings.c atmodem/call-waiting.c \
|
||||
atmodem/call-forwarding.c atmodem/call-meter.c \
|
||||
atmodem/network-registration.c \
|
||||
atmodem/ussd.c atmodem/voicecall.c
|
||||
|
||||
noinst_LTLIBRARIES = libbuiltin.la
|
||||
|
||||
libbuiltin_la_SOURCES = $(builtin_sources)
|
||||
libbuiltin_la_LDFLAGS =
|
||||
libbuiltin_la_CFLAGS = $(AM_CFLAGS) $(builtin_cflags) -DOFONO_PLUGIN_BUILTIN
|
||||
libbuiltin_la_CFLAGS = $(AM_CFLAGS) $(builtin_cflags) \
|
||||
-DOFONO_PLUGIN_BUILTIN -DOFONO_API_SUBJECT_TO_CHANGE
|
||||
|
||||
BUILT_SOURCES = builtin.h
|
||||
|
||||
|
@ -18,7 +24,7 @@ nodist_libbuiltin_la_SOURCES = $(BUILT_SOURCES)
|
|||
|
||||
AM_CFLAGS = -fvisibility=hidden @GLIB_CFLAGS@ @GDBUS_CFLAGS@ @GATCHAT_CFLAGS@
|
||||
|
||||
INCLUDES = -I$(top_builddir)/include
|
||||
INCLUDES = -I$(top_builddir)/include -I$(top_srcdir)/src
|
||||
|
||||
CLEANFILES = $(BUILT_SOURCES)
|
||||
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
*
|
||||
* 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
|
||||
|
||||
struct at_data {
|
||||
GAtChat *parser;
|
||||
struct ofono_modem *modem;
|
||||
GIOChannel *io;
|
||||
char *driver;
|
||||
|
||||
struct netreg_data *netreg;
|
||||
struct voicecall_data *voicecall;
|
||||
};
|
||||
|
||||
void decode_at_error(struct ofono_error *error, const char *final);
|
||||
void dump_response(const char *func, gboolean ok, GAtResult *result);
|
||||
|
||||
struct cb_data {
|
||||
void *cb;
|
||||
void *data;
|
||||
struct ofono_modem *modem;
|
||||
void *user;
|
||||
};
|
||||
|
||||
static inline struct cb_data *cb_data_new(struct ofono_modem *modem,
|
||||
void *cb, void *data)
|
||||
{
|
||||
struct cb_data *ret;
|
||||
|
||||
ret = g_try_new0(struct cb_data, 1);
|
||||
|
||||
if (!ret)
|
||||
return ret;
|
||||
|
||||
ret->cb = cb;
|
||||
ret->data = data;
|
||||
ret->modem = modem;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define DECLARE_FAILURE(e) \
|
||||
struct ofono_error e; \
|
||||
e.type = OFONO_ERROR_TYPE_FAILURE; \
|
||||
e.error = 0 \
|
||||
|
||||
extern struct ofono_error g_ok;
|
||||
|
||||
extern void at_network_registration_init(struct ofono_modem *modem);
|
||||
extern void at_network_registration_exit(struct ofono_modem *modem);
|
||||
|
||||
extern void at_call_forwarding_init(struct ofono_modem *modem);
|
||||
extern void at_call_forwarding_exit(struct ofono_modem *modem);
|
||||
|
||||
extern void at_call_waiting_init(struct ofono_modem *modem);
|
||||
extern void at_call_waiting_exit(struct ofono_modem *modem);
|
||||
|
||||
extern void at_call_settings_init(struct ofono_modem *modem);
|
||||
extern void at_call_settings_exit(struct ofono_modem *modem);
|
||||
|
||||
extern void at_ussd_init(struct ofono_modem *modem);
|
||||
extern void at_ussd_exit(struct ofono_modem *modem);
|
||||
|
||||
extern void at_voicecall_init(struct ofono_modem *modem);
|
||||
extern void at_voicecall_exit(struct ofono_modem *modem);
|
||||
|
||||
extern void at_call_meter_init(struct ofono_modem *modem);
|
||||
extern void at_call_meter_exit(struct ofono_modem *modem);
|
|
@ -0,0 +1,247 @@
|
|||
/*
|
||||
*
|
||||
* 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 "driver.h"
|
||||
|
||||
#include "gatchat.h"
|
||||
#include "gatresult.h"
|
||||
|
||||
#include "at.h"
|
||||
|
||||
static const char *none_prefix[] = { NULL };
|
||||
static const char *ccfc_prefix[] = { "+CCFC:", NULL };
|
||||
|
||||
static void ccfc_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
|
||||
{
|
||||
struct cb_data *cbd = user_data;
|
||||
ofono_call_forwarding_query_cb_t cb = cbd->cb;
|
||||
struct ofono_error error;
|
||||
GAtResultIter iter;
|
||||
int num = 0;
|
||||
struct ofono_cf_condition *list = NULL;
|
||||
int i;
|
||||
|
||||
dump_response("ccfc_query_cb", ok, result);
|
||||
decode_at_error(&error, g_at_result_final_response(result));
|
||||
|
||||
if (!ok)
|
||||
goto out;
|
||||
|
||||
g_at_result_iter_init(&iter, result);
|
||||
|
||||
while (g_at_result_iter_next(&iter, "+CCFC:"))
|
||||
num += 1;
|
||||
|
||||
/* Specification is really unclear about this
|
||||
* generate status=0 for all classes just in case
|
||||
*/
|
||||
if (num == 0) {
|
||||
list = g_new0(struct ofono_cf_condition, 1);
|
||||
num = 1;
|
||||
|
||||
list->status = 0;
|
||||
list->cls = GPOINTER_TO_INT(cbd->user);
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
list = g_new(struct ofono_cf_condition, num);
|
||||
|
||||
g_at_result_iter_init(&iter, result);
|
||||
|
||||
for (num = 0; g_at_result_iter_next(&iter, "+CCFC:"); num++) {
|
||||
const char *str;
|
||||
|
||||
g_at_result_iter_next_number(&iter, &(list[num].status));
|
||||
g_at_result_iter_next_number(&iter, &(list[num].cls));
|
||||
|
||||
list[num].phone_number[0] = '\0';
|
||||
list[num].number_type = 129;
|
||||
list[num].time = 20;
|
||||
|
||||
if (!g_at_result_iter_next_string(&iter, &str))
|
||||
continue;
|
||||
|
||||
strncpy(list[num].phone_number, str,
|
||||
OFONO_MAX_PHONE_NUMBER_LENGTH);
|
||||
list[num].phone_number[OFONO_MAX_PHONE_NUMBER_LENGTH] = '\0';
|
||||
|
||||
g_at_result_iter_next_number(&iter, &(list[num].number_type));
|
||||
|
||||
if (!g_at_result_iter_skip_next(&iter))
|
||||
continue;
|
||||
|
||||
if (!g_at_result_iter_skip_next(&iter))
|
||||
continue;
|
||||
|
||||
g_at_result_iter_next_number(&iter, &(list[num].time));
|
||||
}
|
||||
|
||||
for (i = 0; i < num; i++)
|
||||
ofono_debug("ccfc_cb: %d, %d, %s(%d) - %d sec",
|
||||
list[i].status, list[i].cls,
|
||||
list[i].phone_number, list[i].number_type,
|
||||
list[i].time);
|
||||
|
||||
out:
|
||||
cb(&error, num, list, cbd->data);
|
||||
g_free(list);
|
||||
}
|
||||
|
||||
static void at_ccfc_query(struct ofono_modem *modem, int type, int cls,
|
||||
ofono_call_forwarding_query_cb_t cb, void *data)
|
||||
{
|
||||
struct at_data *at = ofono_modem_userdata(modem);
|
||||
struct cb_data *cbd = cb_data_new(modem, cb, data);
|
||||
char buf[64];
|
||||
|
||||
if (!cbd)
|
||||
goto error;
|
||||
|
||||
cbd->user = GINT_TO_POINTER(cls);
|
||||
|
||||
if (cls == 7)
|
||||
sprintf(buf, "AT+CCFC=%d,2", type);
|
||||
else
|
||||
sprintf(buf, "AT+CCFC=%d,2,,,%d", type, cls);
|
||||
|
||||
if (g_at_chat_send(at->parser, buf, ccfc_prefix,
|
||||
ccfc_query_cb, cbd, g_free) > 0)
|
||||
return;
|
||||
|
||||
error:
|
||||
if (cbd)
|
||||
g_free(cbd);
|
||||
|
||||
{
|
||||
DECLARE_FAILURE(error);
|
||||
cb(&error, 0, NULL, data);
|
||||
}
|
||||
}
|
||||
|
||||
static void ccfc_set_cb(gboolean ok, GAtResult *result, gpointer user_data)
|
||||
{
|
||||
struct cb_data *cbd = user_data;
|
||||
ofono_generic_cb_t cb = cbd->cb;
|
||||
struct ofono_error error;
|
||||
|
||||
dump_response("ccfc_set_cb", ok, result);
|
||||
decode_at_error(&error, g_at_result_final_response(result));
|
||||
|
||||
cb(&error, cbd->data);
|
||||
}
|
||||
|
||||
static void at_ccfc_set(struct ofono_modem *modem, const char *buf,
|
||||
ofono_generic_cb_t cb, void *data)
|
||||
{
|
||||
struct at_data *at = ofono_modem_userdata(modem);
|
||||
struct cb_data *cbd = cb_data_new(modem, cb, data);
|
||||
|
||||
if (!cbd)
|
||||
goto error;
|
||||
|
||||
if (g_at_chat_send(at->parser, buf, none_prefix,
|
||||
ccfc_set_cb, cbd, g_free) > 0)
|
||||
return;
|
||||
|
||||
error:
|
||||
if (cbd)
|
||||
g_free(cbd);
|
||||
|
||||
{
|
||||
DECLARE_FAILURE(error);
|
||||
cb(&error, data);
|
||||
}
|
||||
}
|
||||
|
||||
static void at_ccfc_erasure(struct ofono_modem *modem, int type, int cls,
|
||||
ofono_generic_cb_t cb, void *data)
|
||||
{
|
||||
char buf[128];
|
||||
|
||||
sprintf(buf, "AT+CCFC=%d,4,,,%d", type, cls);
|
||||
at_ccfc_set(modem, buf, cb, data);
|
||||
}
|
||||
|
||||
static void at_ccfc_deactivation(struct ofono_modem *modem, int type, int cls,
|
||||
ofono_generic_cb_t cb, void *data)
|
||||
{
|
||||
char buf[128];
|
||||
|
||||
sprintf(buf, "AT+CCFC=%d,0,,,%d", type, cls);
|
||||
at_ccfc_set(modem, buf, cb, data);
|
||||
}
|
||||
|
||||
static void at_ccfc_activation(struct ofono_modem *modem, int type, int cls,
|
||||
ofono_generic_cb_t cb, void *data)
|
||||
{
|
||||
char buf[128];
|
||||
|
||||
sprintf(buf, "AT+CCFC=%d,1,,,%d", type, cls);
|
||||
at_ccfc_set(modem, buf, cb, data);
|
||||
}
|
||||
|
||||
static void at_ccfc_registration(struct ofono_modem *modem, int type, int cls,
|
||||
const char *number, int number_type,
|
||||
int time, ofono_generic_cb_t cb,
|
||||
void *data)
|
||||
{
|
||||
char buf[128];
|
||||
int offset;
|
||||
|
||||
offset = sprintf(buf, "AT+CCFC=%d,3,%s,%d,%d", type,
|
||||
number, number_type, cls);
|
||||
|
||||
if (type == 2 || type == 4 || type == 5)
|
||||
sprintf(buf+offset, ",,,%d", time);
|
||||
|
||||
at_ccfc_set(modem, buf, cb, data);
|
||||
}
|
||||
|
||||
static struct ofono_call_forwarding_ops ops = {
|
||||
.registration = at_ccfc_registration,
|
||||
.activation = at_ccfc_activation,
|
||||
.query = at_ccfc_query,
|
||||
.deactivation = at_ccfc_deactivation,
|
||||
.erasure = at_ccfc_erasure
|
||||
};
|
||||
|
||||
void at_call_forwarding_init(struct ofono_modem *modem)
|
||||
{
|
||||
ofono_call_forwarding_register(modem, &ops);
|
||||
}
|
||||
|
||||
void at_call_forwarding_exit(struct ofono_modem *modem)
|
||||
{
|
||||
ofono_call_forwarding_unregister(modem);
|
||||
}
|
|
@ -0,0 +1,387 @@
|
|||
/*
|
||||
*
|
||||
* 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 "driver.h"
|
||||
|
||||
#include "gatchat.h"
|
||||
#include "gatresult.h"
|
||||
|
||||
#include "at.h"
|
||||
|
||||
static const char *none_prefix[] = { NULL };
|
||||
static const char *caoc_prefix[] = { "+CAOC:", NULL };
|
||||
static const char *cacm_prefix[] = { "+CACM:", NULL };
|
||||
static const char *camm_prefix[] = { "+CAMM:", NULL };
|
||||
static const char *cpuc_prefix[] = { "+CPUC:", NULL };
|
||||
|
||||
static void caoc_cacm_camm_query_cb(gboolean ok,
|
||||
GAtResult *result, gpointer user_data)
|
||||
{
|
||||
struct cb_data *cbd = user_data;
|
||||
ofono_call_meter_query_cb_t cb = cbd->cb;
|
||||
struct ofono_error error;
|
||||
GAtResultIter iter;
|
||||
const char *meter_hex;
|
||||
char *end;
|
||||
int meter;
|
||||
|
||||
dump_response("caoc_cacm_camm_query_cb", ok, result);
|
||||
decode_at_error(&error, g_at_result_final_response(result));
|
||||
|
||||
if (!ok) {
|
||||
cb(&error, -1, cbd->data);
|
||||
return;
|
||||
}
|
||||
|
||||
g_at_result_iter_init(&iter, result);
|
||||
|
||||
if (!g_at_result_iter_next(&iter, cbd->user)) {
|
||||
DECLARE_FAILURE(e);
|
||||
|
||||
cb(&e, -1, cbd->data);
|
||||
return;
|
||||
}
|
||||
|
||||
g_at_result_iter_next_string(&iter, &meter_hex);
|
||||
meter = strtol(meter_hex, &end, 16);
|
||||
if (*end) {
|
||||
DECLARE_FAILURE(e);
|
||||
|
||||
cb(&e, -1, cbd->data);
|
||||
return;
|
||||
}
|
||||
|
||||
cb(&error, meter, cbd->data);
|
||||
}
|
||||
|
||||
static void cccm_notify(GAtResult *result, gpointer user_data)
|
||||
{
|
||||
struct ofono_modem *modem = user_data;
|
||||
GAtResultIter iter;
|
||||
const char *meter_hex;
|
||||
char *end;
|
||||
int meter;
|
||||
|
||||
dump_response("cccm_notify", TRUE, result);
|
||||
|
||||
g_at_result_iter_init(&iter, result);
|
||||
|
||||
if (!g_at_result_iter_next(&iter, "+CCCM:"))
|
||||
return;
|
||||
|
||||
g_at_result_iter_next_string(&iter, &meter_hex);
|
||||
meter = strtol(meter_hex, &end, 16);
|
||||
if (*end) {
|
||||
ofono_error("Invalid CCCM value");
|
||||
return;
|
||||
}
|
||||
|
||||
ofono_call_meter_changed_notify(modem, meter);
|
||||
}
|
||||
|
||||
static void at_caoc_query(struct ofono_modem *modem, ofono_call_meter_query_cb_t cb,
|
||||
void *data)
|
||||
{
|
||||
struct at_data *at = ofono_modem_userdata(modem);
|
||||
struct cb_data *cbd = cb_data_new(modem, cb, data);
|
||||
|
||||
if (!cbd)
|
||||
goto error;
|
||||
|
||||
cbd->user = "+CAOC:";
|
||||
if (g_at_chat_send(at->parser, "AT+CAOC=0", caoc_prefix,
|
||||
caoc_cacm_camm_query_cb, cbd, g_free) > 0)
|
||||
return;
|
||||
|
||||
error:
|
||||
if (cbd)
|
||||
g_free(cbd);
|
||||
|
||||
{
|
||||
DECLARE_FAILURE(error);
|
||||
cb(&error, -1, modem);
|
||||
}
|
||||
}
|
||||
|
||||
static void at_cacm_query(struct ofono_modem *modem, ofono_call_meter_query_cb_t cb,
|
||||
void *data)
|
||||
{
|
||||
struct at_data *at = ofono_modem_userdata(modem);
|
||||
struct cb_data *cbd = cb_data_new(modem, cb, data);
|
||||
|
||||
if (!cbd)
|
||||
goto error;
|
||||
|
||||
cbd->user = "+CACM:";
|
||||
if (g_at_chat_send(at->parser, "AT+CACM?", cacm_prefix,
|
||||
caoc_cacm_camm_query_cb, cbd, g_free) > 0)
|
||||
return;
|
||||
|
||||
error:
|
||||
if (cbd)
|
||||
g_free(cbd);
|
||||
|
||||
{
|
||||
DECLARE_FAILURE(error);
|
||||
cb(&error, -1, modem);
|
||||
}
|
||||
}
|
||||
|
||||
static void generic_set_cb(gboolean ok, GAtResult *result, gpointer user_data)
|
||||
{
|
||||
struct cb_data *cbd = user_data;
|
||||
ofono_generic_cb_t cb = cbd->cb;
|
||||
struct ofono_error error;
|
||||
|
||||
dump_response("generic_set_cb", ok, result);
|
||||
decode_at_error(&error, g_at_result_final_response(result));
|
||||
|
||||
cb(&error, cbd->data);
|
||||
}
|
||||
|
||||
static void at_cacm_set(struct ofono_modem *modem, const char *passwd,
|
||||
ofono_generic_cb_t cb, void *data)
|
||||
{
|
||||
struct at_data *at = ofono_modem_userdata(modem);
|
||||
struct cb_data *cbd = cb_data_new(modem, cb, data);
|
||||
char buf[64];
|
||||
|
||||
if (!cbd)
|
||||
goto error;
|
||||
|
||||
snprintf(buf, sizeof(buf), "AT+CACM=\"%s\"", passwd);
|
||||
|
||||
if (g_at_chat_send(at->parser, buf, none_prefix,
|
||||
generic_set_cb, cbd, g_free) > 0)
|
||||
return;
|
||||
|
||||
error:
|
||||
if (cbd)
|
||||
g_free(cbd);
|
||||
|
||||
{
|
||||
DECLARE_FAILURE(error);
|
||||
cb(&error, modem);
|
||||
}
|
||||
}
|
||||
|
||||
static void at_camm_query(struct ofono_modem *modem, ofono_call_meter_query_cb_t cb,
|
||||
void *data)
|
||||
{
|
||||
struct at_data *at = ofono_modem_userdata(modem);
|
||||
struct cb_data *cbd = cb_data_new(modem, cb, data);
|
||||
|
||||
if (!cbd)
|
||||
goto error;
|
||||
|
||||
cbd->user = "+CAMM:";
|
||||
if (g_at_chat_send(at->parser, "AT+CAMM?", camm_prefix,
|
||||
caoc_cacm_camm_query_cb, cbd, g_free) > 0)
|
||||
return;
|
||||
|
||||
error:
|
||||
if (cbd)
|
||||
g_free(cbd);
|
||||
|
||||
{
|
||||
DECLARE_FAILURE(error);
|
||||
cb(&error, -1, modem);
|
||||
}
|
||||
}
|
||||
|
||||
static void at_camm_set(struct ofono_modem *modem, int accmax, const char *passwd,
|
||||
ofono_generic_cb_t cb, void *data)
|
||||
{
|
||||
struct at_data *at = ofono_modem_userdata(modem);
|
||||
struct cb_data *cbd = cb_data_new(modem, cb, data);
|
||||
char buf[64];
|
||||
|
||||
if (!cbd)
|
||||
goto error;
|
||||
|
||||
sprintf(buf, "AT+CAMM=\"%06X\",\"%s\"", accmax, passwd);
|
||||
|
||||
if (g_at_chat_send(at->parser, buf, none_prefix,
|
||||
generic_set_cb, cbd, g_free) > 0)
|
||||
return;
|
||||
|
||||
error:
|
||||
if (cbd)
|
||||
g_free(cbd);
|
||||
|
||||
{
|
||||
DECLARE_FAILURE(error);
|
||||
cb(&error, modem);
|
||||
}
|
||||
}
|
||||
|
||||
static void cpuc_query_cb(gboolean ok,
|
||||
GAtResult *result, gpointer user_data)
|
||||
{
|
||||
struct cb_data *cbd = user_data;
|
||||
ofono_call_meter_puct_query_cb_t cb = cbd->cb;
|
||||
struct ofono_error error;
|
||||
GAtResultIter iter;
|
||||
const char *currency, *ppu;
|
||||
char currency_buf[64];
|
||||
double ppuval;
|
||||
|
||||
dump_response("cpuc_query_cb", ok, result);
|
||||
decode_at_error(&error, g_at_result_final_response(result));
|
||||
|
||||
if (!ok) {
|
||||
cb(&error, 0, 0, cbd->data);
|
||||
return;
|
||||
}
|
||||
|
||||
g_at_result_iter_init(&iter, result);
|
||||
|
||||
if (!g_at_result_iter_next(&iter, cbd->user)) {
|
||||
DECLARE_FAILURE(e);
|
||||
|
||||
cb(&e, 0, 0, cbd->data);
|
||||
return;
|
||||
}
|
||||
|
||||
g_at_result_iter_next_string(&iter, ¤cy);
|
||||
strncpy(currency_buf, currency, sizeof(currency_buf));
|
||||
|
||||
g_at_result_iter_next_string(&iter, &ppu);
|
||||
ppuval = strtod(ppu, NULL);
|
||||
|
||||
cb(&error, currency_buf, ppuval, cbd->data);
|
||||
}
|
||||
|
||||
static void at_cpuc_query(struct ofono_modem *modem,
|
||||
ofono_call_meter_puct_query_cb_t cb, void *data)
|
||||
{
|
||||
struct at_data *at = ofono_modem_userdata(modem);
|
||||
struct cb_data *cbd = cb_data_new(modem, cb, data);
|
||||
|
||||
if (!cbd)
|
||||
goto error;
|
||||
|
||||
cbd->user = "+CPUC:";
|
||||
if (g_at_chat_send(at->parser, "AT+CPUC?", cpuc_prefix,
|
||||
cpuc_query_cb, cbd, g_free) > 0)
|
||||
return;
|
||||
|
||||
error:
|
||||
if (cbd)
|
||||
g_free(cbd);
|
||||
|
||||
{
|
||||
DECLARE_FAILURE(error);
|
||||
cb(&error, 0, 0, modem);
|
||||
}
|
||||
}
|
||||
|
||||
static void at_cpuc_set(struct ofono_modem *modem, const char *currency,
|
||||
double ppu, const char *passwd, ofono_generic_cb_t cb,
|
||||
void *data)
|
||||
{
|
||||
struct at_data *at = ofono_modem_userdata(modem);
|
||||
struct cb_data *cbd = cb_data_new(modem, cb, data);
|
||||
char buf[64];
|
||||
|
||||
if (!cbd)
|
||||
goto error;
|
||||
|
||||
snprintf(buf, sizeof(buf), "AT+CPUC=\"%s\",\"%f\",\"%s\"",
|
||||
currency, ppu, passwd);
|
||||
|
||||
if (g_at_chat_send(at->parser, buf, none_prefix,
|
||||
generic_set_cb, cbd, g_free) > 0)
|
||||
return;
|
||||
|
||||
error:
|
||||
if (cbd)
|
||||
g_free(cbd);
|
||||
|
||||
{
|
||||
DECLARE_FAILURE(error);
|
||||
cb(&error, modem);
|
||||
}
|
||||
}
|
||||
|
||||
static void ccwv_notify(GAtResult *result, gpointer user_data)
|
||||
{
|
||||
struct ofono_modem *modem = user_data;
|
||||
GAtResultIter iter;
|
||||
|
||||
dump_response("ccwv_notify", TRUE, result);
|
||||
|
||||
g_at_result_iter_init(&iter, result);
|
||||
if (!g_at_result_iter_next(&iter, "+CCWV"))
|
||||
return;
|
||||
|
||||
ofono_call_meter_maximum_notify(modem);
|
||||
}
|
||||
|
||||
static struct ofono_call_meter_ops ops = {
|
||||
.call_meter_query = at_caoc_query,
|
||||
.acm_query = at_cacm_query,
|
||||
.acm_reset = at_cacm_set,
|
||||
.acm_max_query = at_camm_query,
|
||||
.acm_max_set = at_camm_set,
|
||||
.puct_query = at_cpuc_query,
|
||||
.puct_set = at_cpuc_set,
|
||||
};
|
||||
|
||||
static void at_call_meter_initialized(gboolean ok, GAtResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
struct ofono_modem *modem = user_data;
|
||||
struct at_data *at = ofono_modem_userdata(modem);
|
||||
|
||||
g_at_chat_register(at->parser, "+CCCM:",
|
||||
cccm_notify, FALSE, modem, NULL);
|
||||
g_at_chat_register(at->parser, "+CCWV",
|
||||
ccwv_notify, FALSE, modem, NULL);
|
||||
|
||||
ofono_call_meter_register(modem, &ops);
|
||||
}
|
||||
|
||||
void at_call_meter_init(struct ofono_modem *modem)
|
||||
{
|
||||
struct at_data *at = ofono_modem_userdata(modem);
|
||||
|
||||
g_at_chat_send(at->parser, "AT+CAOC=2", NULL, NULL, NULL, NULL);
|
||||
g_at_chat_send(at->parser, "AT+CCWE=1", NULL,
|
||||
at_call_meter_initialized, modem, NULL);
|
||||
}
|
||||
|
||||
void at_call_meter_exit(struct ofono_modem *modem)
|
||||
{
|
||||
ofono_call_meter_unregister(modem);
|
||||
}
|
|
@ -0,0 +1,271 @@
|
|||
/*
|
||||
*
|
||||
* 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 "driver.h"
|
||||
|
||||
#include "gatchat.h"
|
||||
#include "gatresult.h"
|
||||
|
||||
#include "at.h"
|
||||
|
||||
static const char *none_prefix[] = { NULL };
|
||||
static const char *clir_prefix[] = { "+CLIR:", NULL };
|
||||
static const char *colp_prefix[] = { "+COLP:", NULL };
|
||||
static const char *clip_prefix[] = { "+CLIP:", NULL };
|
||||
|
||||
static void clip_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
|
||||
{
|
||||
struct cb_data *cbd = user_data;
|
||||
ofono_call_setting_status_cb_t cb = cbd->cb;
|
||||
struct ofono_error error;
|
||||
GAtResultIter iter;
|
||||
int status = -1;
|
||||
|
||||
dump_response("clip_query_cb", ok, result);
|
||||
decode_at_error(&error, g_at_result_final_response(result));
|
||||
|
||||
if (!ok) {
|
||||
cb(&error, -1, cbd->data);
|
||||
return;
|
||||
}
|
||||
|
||||
g_at_result_iter_init(&iter, result);
|
||||
|
||||
if (!g_at_result_iter_next(&iter, "+CLIP:")) {
|
||||
DECLARE_FAILURE(e);
|
||||
|
||||
cb(&e, -1, cbd->data);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Skip the local presentation setting */
|
||||
g_at_result_iter_skip_next(&iter);
|
||||
g_at_result_iter_next_number(&iter, &status);
|
||||
|
||||
ofono_debug("clip_query_cb: network: %d", status);
|
||||
|
||||
cb(&error, status, cbd->data);
|
||||
}
|
||||
|
||||
static void at_clip_query(struct ofono_modem *modem,
|
||||
ofono_call_setting_status_cb_t cb, void *data)
|
||||
{
|
||||
struct at_data *at = ofono_modem_userdata(modem);
|
||||
struct cb_data *cbd = cb_data_new(modem, cb, data);
|
||||
|
||||
if (!cbd)
|
||||
goto error;
|
||||
|
||||
if (g_at_chat_send(at->parser, "AT+CLIP?", clip_prefix,
|
||||
clip_query_cb, cbd, g_free) > 0)
|
||||
return;
|
||||
|
||||
error:
|
||||
if (cbd)
|
||||
g_free(cbd);
|
||||
|
||||
{
|
||||
DECLARE_FAILURE(error);
|
||||
cb(&error, -1, data);
|
||||
}
|
||||
}
|
||||
|
||||
static void colp_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
|
||||
{
|
||||
struct cb_data *cbd = user_data;
|
||||
ofono_call_setting_status_cb_t cb = cbd->cb;
|
||||
struct ofono_error error;
|
||||
GAtResultIter iter;
|
||||
int status;
|
||||
|
||||
dump_response("colp_query_cb", ok, result);
|
||||
decode_at_error(&error, g_at_result_final_response(result));
|
||||
|
||||
if (!ok) {
|
||||
cb(&error, -1, cbd->data);
|
||||
return;
|
||||
}
|
||||
|
||||
g_at_result_iter_init(&iter, result);
|
||||
|
||||
if (!g_at_result_iter_next(&iter, "+COLP:")) {
|
||||
DECLARE_FAILURE(e);
|
||||
|
||||
cb(&e, -1, cbd->data);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Skip the local presentation setting */
|
||||
g_at_result_iter_skip_next(&iter);
|
||||
g_at_result_iter_next_number(&iter, &status);
|
||||
|
||||
ofono_debug("colp_query_cb: network: %d", status);
|
||||
|
||||
cb(&error, status, cbd->data);
|
||||
}
|
||||
|
||||
static void at_colp_query(struct ofono_modem *modem,
|
||||
ofono_call_setting_status_cb_t cb, void *data)
|
||||
{
|
||||
struct at_data *at = ofono_modem_userdata(modem);
|
||||
struct cb_data *cbd = cb_data_new(modem, cb, data);
|
||||
|
||||
if (!cbd)
|
||||
goto error;
|
||||
|
||||
if (g_at_chat_send(at->parser, "AT+COLP?", colp_prefix,
|
||||
colp_query_cb, cbd, g_free) > 0)
|
||||
return;
|
||||
|
||||
error:
|
||||
if (cbd)
|
||||
g_free(cbd);
|
||||
|
||||
{
|
||||
DECLARE_FAILURE(error);
|
||||
cb(&error, -1, data);
|
||||
}
|
||||
}
|
||||
|
||||
static void clir_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
|
||||
{
|
||||
struct cb_data *cbd = user_data;
|
||||
ofono_clir_setting_cb_t cb = cbd->cb;
|
||||
struct ofono_error error;
|
||||
GAtResultIter iter;
|
||||
int override = 0, network = 2;
|
||||
|
||||
dump_response("clir_query_cb", ok, result);
|
||||
decode_at_error(&error, g_at_result_final_response(result));
|
||||
|
||||
if (!ok) {
|
||||
cb(&error, -1, -1, cbd->data);
|
||||
return;
|
||||
}
|
||||
|
||||
g_at_result_iter_init(&iter, result);
|
||||
|
||||
if (!g_at_result_iter_next(&iter, "+CLIR:")) {
|
||||
DECLARE_FAILURE(e);
|
||||
|
||||
cb(&e, -1, -1, cbd->data);
|
||||
return;
|
||||
}
|
||||
|
||||
g_at_result_iter_next_number(&iter, &override);
|
||||
g_at_result_iter_next_number(&iter, &network);
|
||||
|
||||
ofono_debug("clir_query_cb: override: %d, network: %d",
|
||||
override, network);
|
||||
|
||||
cb(&error, override, network, cbd->data);
|
||||
}
|
||||
|
||||
static void at_clir_query(struct ofono_modem *modem,
|
||||
ofono_clir_setting_cb_t cb, void *data)
|
||||
{
|
||||
struct at_data *at = ofono_modem_userdata(modem);
|
||||
struct cb_data *cbd = cb_data_new(modem, cb, data);
|
||||
|
||||
if (!cbd)
|
||||
goto error;
|
||||
|
||||
if (g_at_chat_send(at->parser, "AT+CLIR?", clir_prefix,
|
||||
clir_query_cb, cbd, g_free) > 0)
|
||||
return;
|
||||
|
||||
error:
|
||||
if (cbd)
|
||||
g_free(cbd);
|
||||
|
||||
{
|
||||
DECLARE_FAILURE(error);
|
||||
cb(&error, -1, -1, data);
|
||||
}
|
||||
}
|
||||
|
||||
static void clir_set_cb(gboolean ok, GAtResult *result, gpointer user_data)
|
||||
{
|
||||
struct cb_data *cbd = user_data;
|
||||
ofono_generic_cb_t cb = cbd->cb;
|
||||
struct ofono_error error;
|
||||
|
||||
dump_response("clir_set_cb", ok, result);
|
||||
decode_at_error(&error, g_at_result_final_response(result));
|
||||
|
||||
cb(&error, cbd->data);
|
||||
}
|
||||
|
||||
static void at_clir_set(struct ofono_modem *modem, int mode,
|
||||
ofono_generic_cb_t cb, void *data)
|
||||
{
|
||||
struct at_data *at = ofono_modem_userdata(modem);
|
||||
struct cb_data *cbd = cb_data_new(modem, cb, data);
|
||||
char buf[64];
|
||||
|
||||
if (!cbd)
|
||||
goto error;
|
||||
|
||||
sprintf(buf, "AT+CLIR=%d", mode);
|
||||
|
||||
if (g_at_chat_send(at->parser, buf, none_prefix,
|
||||
clir_set_cb, cbd, g_free) > 0)
|
||||
return;
|
||||
|
||||
error:
|
||||
if (cbd)
|
||||
g_free(cbd);
|
||||
|
||||
{
|
||||
DECLARE_FAILURE(error);
|
||||
cb(&error, data);
|
||||
}
|
||||
}
|
||||
|
||||
static struct ofono_call_settings_ops ops = {
|
||||
.clip_query = at_clip_query,
|
||||
.colp_query = at_colp_query,
|
||||
.clir_query = at_clir_query,
|
||||
.clir_set = at_clir_set,
|
||||
.colr_query = NULL
|
||||
};
|
||||
|
||||
void at_call_settings_init(struct ofono_modem *modem)
|
||||
{
|
||||
ofono_call_settings_register(modem, &ops);
|
||||
}
|
||||
|
||||
void at_call_settings_exit(struct ofono_modem *modem)
|
||||
{
|
||||
ofono_call_settings_unregister(modem);
|
||||
}
|
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
*
|
||||
* 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 "driver.h"
|
||||
|
||||
#include "gatchat.h"
|
||||
#include "gatresult.h"
|
||||
|
||||
#include "at.h"
|
||||
|
||||
static const char *none_prefix[] = { NULL };
|
||||
static const char *ccwa_prefix[] = { "+CCWA:", NULL };
|
||||
|
||||
static void ccwa_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
|
||||
{
|
||||
struct cb_data *cbd = user_data;
|
||||
ofono_call_waiting_status_cb_t cb = cbd->cb;
|
||||
struct ofono_error error;
|
||||
GAtResultIter iter;
|
||||
int num = 0;
|
||||
struct ofono_cw_condition *list = NULL;
|
||||
int i;
|
||||
|
||||
dump_response("ccwa_query_cb", ok, result);
|
||||
decode_at_error(&error, g_at_result_final_response(result));
|
||||
|
||||
if (!ok)
|
||||
goto out;
|
||||
|
||||
g_at_result_iter_init(&iter, result);
|
||||
|
||||
while (g_at_result_iter_next(&iter, "+CCWA:"))
|
||||
num += 1;
|
||||
|
||||
/* Specification is really unclear about this
|
||||
* generate status=0 for all classes just in case
|
||||
*/
|
||||
if (num == 0) {
|
||||
list = g_new(struct ofono_cw_condition, 1);
|
||||
num = 1;
|
||||
|
||||
list->status = 0;
|
||||
list->cls = GPOINTER_TO_INT(cbd->user);
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
list = g_new(struct ofono_cw_condition, num);
|
||||
|
||||
g_at_result_iter_init(&iter, result);
|
||||
num = 0;
|
||||
|
||||
while (g_at_result_iter_next(&iter, "+CCWA:")) {
|
||||
g_at_result_iter_next_number(&iter, &(list[num].status));
|
||||
g_at_result_iter_next_number(&iter, &(list[num].cls));
|
||||
|
||||
num += 1;
|
||||
}
|
||||
|
||||
for (i = 0; i < num; i++)
|
||||
ofono_debug("ccwa_cb: %d, %d", list[i].status, list[i].cls);
|
||||
|
||||
out:
|
||||
cb(&error, num, list, cbd->data);
|
||||
g_free(list);
|
||||
}
|
||||
|
||||
static void at_ccwa_query(struct ofono_modem *modem, int cls,
|
||||
ofono_call_waiting_status_cb_t cb, void *data)
|
||||
{
|
||||
struct at_data *at = ofono_modem_userdata(modem);
|
||||
struct cb_data *cbd = cb_data_new(modem, cb, data);
|
||||
char buf[64];
|
||||
|
||||
if (!cbd)
|
||||
goto error;
|
||||
|
||||
cbd->user = GINT_TO_POINTER(cls);
|
||||
|
||||
if (cls == 7)
|
||||
sprintf(buf, "AT+CCWA=1,2");
|
||||
else
|
||||
sprintf(buf, "AT+CCWA=1,2,%d", cls);
|
||||
|
||||
if (g_at_chat_send(at->parser, buf, ccwa_prefix,
|
||||
ccwa_query_cb, cbd, g_free) > 0)
|
||||
return;
|
||||
|
||||
error:
|
||||
if (cbd)
|
||||
g_free(cbd);
|
||||
|
||||
{
|
||||
DECLARE_FAILURE(error);
|
||||
cb(&error, 0, NULL, data);
|
||||
}
|
||||
}
|
||||
|
||||
static void ccwa_set_cb(gboolean ok, GAtResult *result, gpointer user_data)
|
||||
{
|
||||
struct cb_data *cbd = user_data;
|
||||
ofono_generic_cb_t cb = cbd->cb;
|
||||
struct ofono_error error;
|
||||
|
||||
dump_response("ccwa_set_cb", ok, result);
|
||||
decode_at_error(&error, g_at_result_final_response(result));
|
||||
|
||||
cb(&error, cbd->data);
|
||||
}
|
||||
|
||||
static void at_ccwa_set(struct ofono_modem *modem, int mode, int cls,
|
||||
ofono_generic_cb_t cb, void *data)
|
||||
{
|
||||
struct at_data *at = ofono_modem_userdata(modem);
|
||||
struct cb_data *cbd = cb_data_new(modem, cb, data);
|
||||
char buf[64];
|
||||
|
||||
if (!cbd)
|
||||
goto error;
|
||||
|
||||
sprintf(buf, "AT+CCWA=1,%d,%d", mode, cls);
|
||||
|
||||
if (g_at_chat_send(at->parser, buf, none_prefix,
|
||||
ccwa_set_cb, cbd, g_free) > 0)
|
||||
return;
|
||||
|
||||
error:
|
||||
if (cbd)
|
||||
g_free(cbd);
|
||||
|
||||
{
|
||||
DECLARE_FAILURE(error);
|
||||
cb(&error, data);
|
||||
}
|
||||
}
|
||||
|
||||
static struct ofono_call_waiting_ops ops = {
|
||||
.query = at_ccwa_query,
|
||||
.set = at_ccwa_set
|
||||
};
|
||||
|
||||
void at_call_waiting_init(struct ofono_modem *modem)
|
||||
{
|
||||
ofono_call_waiting_register(modem, &ops);
|
||||
}
|
||||
|
||||
void at_call_waiting_exit(struct ofono_modem *modem)
|
||||
{
|
||||
ofono_call_waiting_unregister(modem);
|
||||
}
|
|
@ -23,20 +23,491 @@
|
|||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#define OFONO_API_SUBJECT_TO_CHANGE
|
||||
#include <string.h>
|
||||
#include <glib.h>
|
||||
#include <gdbus.h>
|
||||
#include <gatchat.h>
|
||||
|
||||
#include <ofono/plugin.h>
|
||||
#include <ofono/log.h>
|
||||
#include "driver.h"
|
||||
|
||||
#include "at.h"
|
||||
#include "dbus-gsm.h"
|
||||
#include "modem.h"
|
||||
#include "session.h"
|
||||
|
||||
#define AT_MANAGER_INTERFACE "org.ofono.at.Manager"
|
||||
|
||||
static GSList *g_sessions = NULL;
|
||||
static GSList *g_pending = NULL;
|
||||
|
||||
static void modem_list(char ***modems)
|
||||
{
|
||||
GSList *l;
|
||||
int i;
|
||||
struct at_data *at;
|
||||
|
||||
*modems = g_new0(char *, g_slist_length(g_sessions) + 1);
|
||||
|
||||
for (l = g_sessions, i = 0; l; l = l->next, i++) {
|
||||
at = l->data;
|
||||
|
||||
(*modems)[i] = at->modem->path;
|
||||
}
|
||||
}
|
||||
|
||||
void dump_response(const char *func, gboolean ok, GAtResult *result)
|
||||
{
|
||||
GSList *l;
|
||||
|
||||
ofono_debug("%s got result: %d", func, ok);
|
||||
ofono_debug("Final response: %s", result->final_or_pdu);
|
||||
|
||||
for (l = result->lines; l; l = l->next)
|
||||
ofono_debug("Response line: %s", (char *) l->data);
|
||||
}
|
||||
|
||||
void decode_at_error(struct ofono_error *error, const char *final)
|
||||
{
|
||||
if (!strcmp(final, "OK")) {
|
||||
error->type = OFONO_ERROR_TYPE_NO_ERROR;
|
||||
error->error = 0;
|
||||
} else {
|
||||
error->type = OFONO_ERROR_TYPE_FAILURE;
|
||||
error->error = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void at_destroy(struct at_data *at)
|
||||
{
|
||||
if (at->parser)
|
||||
g_at_chat_unref(at->parser);
|
||||
|
||||
if (at->driver)
|
||||
g_free(at->driver);
|
||||
|
||||
g_free(at);
|
||||
}
|
||||
|
||||
static void manager_free(gpointer user)
|
||||
{
|
||||
GSList *l;
|
||||
|
||||
for (l = g_pending; l; l = l->next)
|
||||
g_io_channel_unref(l->data);
|
||||
|
||||
g_slist_free(g_pending);
|
||||
|
||||
for (l = g_sessions; l; l = l->next) {
|
||||
struct at_data *at = l->data;
|
||||
|
||||
at_call_forwarding_exit(at->modem);
|
||||
at_call_waiting_exit(at->modem);
|
||||
at_call_settings_exit(at->modem);
|
||||
at_network_registration_exit(at->modem);
|
||||
at_voicecall_exit(at->modem);
|
||||
at_ussd_exit(at->modem);
|
||||
at_call_meter_exit(at->modem);
|
||||
ofono_modem_unregister(at->modem);
|
||||
|
||||
at_destroy(at);
|
||||
}
|
||||
|
||||
g_slist_free(g_sessions);
|
||||
}
|
||||
|
||||
struct attr_cb_info {
|
||||
ofono_modem_attribute_query_cb_t cb;
|
||||
void *data;
|
||||
const char *prefix;
|
||||
};
|
||||
|
||||
static inline struct attr_cb_info *attr_cb_info_new(ofono_modem_attribute_query_cb_t cb,
|
||||
void *data,
|
||||
const char *prefix)
|
||||
{
|
||||
struct attr_cb_info *ret;
|
||||
|
||||
ret = g_try_new(struct attr_cb_info, 1);
|
||||
|
||||
if (!ret)
|
||||
return ret;
|
||||
|
||||
ret->cb = cb;
|
||||
ret->data = data;
|
||||
ret->prefix = prefix;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const char *fixup_return(const char *line, const char *prefix)
|
||||
{
|
||||
if (g_str_has_prefix(line, prefix) == FALSE)
|
||||
return line;
|
||||
|
||||
line = line + strlen(prefix);
|
||||
|
||||
while (line[0] == ' ')
|
||||
line++;
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
static void attr_cb(gboolean ok, GAtResult *result, gpointer user_data)
|
||||
{
|
||||
struct ofono_error error;
|
||||
struct attr_cb_info *info = user_data;
|
||||
|
||||
decode_at_error(&error, g_at_result_final_response(result));
|
||||
|
||||
dump_response("attr_cb", ok, result);
|
||||
|
||||
if (ok) {
|
||||
GAtResultIter iter;
|
||||
const char *line;
|
||||
int i;
|
||||
|
||||
g_at_result_iter_init(&iter, result);
|
||||
|
||||
/* We have to be careful here, sometimes a stray unsolicited
|
||||
* notification will appear as part of the response and we
|
||||
* cannot rely on having a prefix to recognize the actual
|
||||
* response line. So use the last line only as the response
|
||||
*/
|
||||
for (i = 0; i < g_at_result_num_response_lines(result); i++)
|
||||
g_at_result_iter_next(&iter, NULL);
|
||||
|
||||
line = g_at_result_iter_raw_line(&iter);
|
||||
|
||||
info->cb(&error, fixup_return(line, info->prefix), info->data);
|
||||
} else
|
||||
info->cb(&error, "", info->data);
|
||||
}
|
||||
|
||||
static void at_query_manufacturer(struct ofono_modem *modem,
|
||||
ofono_modem_attribute_query_cb_t cb, void *data)
|
||||
{
|
||||
struct attr_cb_info *info = attr_cb_info_new(cb, data, "+CGMI:");
|
||||
struct at_data *at = ofono_modem_userdata(modem);
|
||||
|
||||
if (!info)
|
||||
goto error;
|
||||
|
||||
if (g_at_chat_send(at->parser, "AT+CGMI", NULL,
|
||||
attr_cb, info, g_free) > 0)
|
||||
return;
|
||||
|
||||
error:
|
||||
if (info)
|
||||
g_free(info);
|
||||
|
||||
{
|
||||
DECLARE_FAILURE(error);
|
||||
cb(&error, NULL, data);
|
||||
}
|
||||
}
|
||||
|
||||
static void at_query_model(struct ofono_modem *modem,
|
||||
ofono_modem_attribute_query_cb_t cb, void *data)
|
||||
{
|
||||
struct attr_cb_info *info = attr_cb_info_new(cb, data, "+CGMM:");
|
||||
struct at_data *at = ofono_modem_userdata(modem);
|
||||
|
||||
if (!info)
|
||||
goto error;
|
||||
|
||||
if (g_at_chat_send(at->parser, "AT+CGMM", NULL,
|
||||
attr_cb, info, g_free) > 0)
|
||||
return;
|
||||
|
||||
error:
|
||||
if (info)
|
||||
g_free(info);
|
||||
|
||||
{
|
||||
DECLARE_FAILURE(error);
|
||||
cb(&error, NULL, data);
|
||||
}
|
||||
}
|
||||
|
||||
static void at_query_revision(struct ofono_modem *modem,
|
||||
ofono_modem_attribute_query_cb_t cb, void *data)
|
||||
{
|
||||
struct attr_cb_info *info = attr_cb_info_new(cb, data, "+CGMR:");
|
||||
struct at_data *at = ofono_modem_userdata(modem);
|
||||
|
||||
if (!info)
|
||||
goto error;
|
||||
|
||||
if (g_at_chat_send(at->parser, "AT+CGMR", NULL,
|
||||
attr_cb, info, g_free) > 0)
|
||||
return;
|
||||
|
||||
error:
|
||||
if (info)
|
||||
g_free(info);
|
||||
|
||||
{
|
||||
DECLARE_FAILURE(error);
|
||||
cb(&error, NULL, data);
|
||||
}
|
||||
}
|
||||
|
||||
static void at_query_serial(struct ofono_modem *modem,
|
||||
ofono_modem_attribute_query_cb_t cb, void *data)
|
||||
{
|
||||
struct attr_cb_info *info = attr_cb_info_new(cb, data, "+CGSN:");
|
||||
struct at_data *at = ofono_modem_userdata(modem);
|
||||
|
||||
if (!info)
|
||||
goto error;
|
||||
|
||||
if (g_at_chat_send(at->parser, "AT+CGSN", NULL,
|
||||
attr_cb, info, g_free) > 0)
|
||||
return;
|
||||
|
||||
error:
|
||||
if (info)
|
||||
g_free(info);
|
||||
|
||||
{
|
||||
DECLARE_FAILURE(error);
|
||||
cb(&error, NULL, data);
|
||||
}
|
||||
}
|
||||
|
||||
static void send_init_commands(const char *vendor, GAtChat *parser)
|
||||
{
|
||||
if (!strcmp(vendor, "ti_calypso")) {
|
||||
g_at_chat_set_wakeup_command(parser, "\r", 1000, 5000);
|
||||
|
||||
g_at_chat_send(parser, "AT%CUNS=0", NULL,
|
||||
NULL, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static struct ofono_modem_attribute_ops ops = {
|
||||
.query_manufacturer = at_query_manufacturer,
|
||||
.query_model = at_query_model,
|
||||
.query_revision = at_query_revision,
|
||||
.query_serial = at_query_serial
|
||||
};
|
||||
|
||||
static void msg_destroy(gpointer user)
|
||||
{
|
||||
DBusMessage *msg = user;
|
||||
|
||||
dbus_message_unref(msg);
|
||||
}
|
||||
|
||||
static void create_cb(GIOChannel *io, gboolean success, gpointer user)
|
||||
{
|
||||
DBusConnection *conn = dbus_gsm_connection();
|
||||
DBusMessage *msg = user;
|
||||
DBusMessage *reply;
|
||||
struct at_data *at = NULL;
|
||||
const char *path;
|
||||
const char *target, *driver;
|
||||
char **modems;
|
||||
|
||||
if (success == FALSE)
|
||||
goto out;
|
||||
|
||||
at = g_new0(struct at_data, 1);
|
||||
|
||||
at->parser = g_at_chat_new(io, 0);
|
||||
|
||||
if (!at->parser)
|
||||
goto out;
|
||||
|
||||
ofono_debug("Seting up AT channel");
|
||||
|
||||
dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &target,
|
||||
DBUS_TYPE_STRING, &driver, DBUS_TYPE_INVALID);
|
||||
|
||||
send_init_commands(driver, at->parser);
|
||||
|
||||
at->modem = ofono_modem_register(&ops);
|
||||
|
||||
if (!at->modem)
|
||||
goto out;
|
||||
|
||||
ofono_modem_set_userdata(at->modem, at);
|
||||
|
||||
at_ussd_init(at->modem);
|
||||
at_call_forwarding_init(at->modem);
|
||||
at_call_settings_init(at->modem);
|
||||
at_call_waiting_init(at->modem);
|
||||
at_network_registration_init(at->modem);
|
||||
at_voicecall_init(at->modem);
|
||||
at_call_meter_init(at->modem);
|
||||
|
||||
at->io = io;
|
||||
at->driver = g_strdup(driver);
|
||||
|
||||
g_pending = g_slist_remove(g_pending, io);
|
||||
g_sessions = g_slist_prepend(g_sessions, at);
|
||||
|
||||
path = at->modem->path;
|
||||
|
||||
reply = dbus_message_new_method_return(msg);
|
||||
|
||||
dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path,
|
||||
DBUS_TYPE_INVALID);
|
||||
|
||||
g_dbus_send_message(conn, reply);
|
||||
|
||||
modem_list(&modems);
|
||||
dbus_gsm_signal_array_property_changed(conn, "/", AT_MANAGER_INTERFACE,
|
||||
"Modems", DBUS_TYPE_OBJECT_PATH,
|
||||
&modems);
|
||||
g_free(modems);
|
||||
|
||||
return;
|
||||
|
||||
out:
|
||||
if (at)
|
||||
at_destroy(at);
|
||||
|
||||
reply = dbus_gsm_failed(msg);
|
||||
g_dbus_send_message(conn, reply);
|
||||
}
|
||||
|
||||
static DBusMessage *manager_create(DBusConnection *conn, DBusMessage *msg,
|
||||
void *data)
|
||||
{
|
||||
const char *target;
|
||||
const char *driver;
|
||||
GIOChannel *io;
|
||||
|
||||
if (!dbus_message_get_args(msg, NULL,
|
||||
DBUS_TYPE_STRING, &target,
|
||||
DBUS_TYPE_STRING, &driver,
|
||||
DBUS_TYPE_INVALID))
|
||||
return dbus_gsm_invalid_args(msg);
|
||||
|
||||
io = modem_session_create(target, create_cb, msg, msg_destroy);
|
||||
|
||||
if (!io)
|
||||
return dbus_gsm_invalid_format(msg);
|
||||
|
||||
dbus_message_ref(msg);
|
||||
|
||||
g_pending = g_slist_prepend(g_pending, io);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static DBusMessage *manager_destroy(DBusConnection *conn, DBusMessage *msg,
|
||||
void *data)
|
||||
{
|
||||
const char *path;
|
||||
GSList *l;
|
||||
|
||||
if (!dbus_message_get_args(msg, NULL,
|
||||
DBUS_TYPE_OBJECT_PATH, &path,
|
||||
DBUS_TYPE_INVALID))
|
||||
return dbus_gsm_invalid_args(msg);
|
||||
|
||||
for (l = g_sessions; l; l = l->next) {
|
||||
struct at_data *at = l->data;
|
||||
char **modems;
|
||||
|
||||
if (strcmp(at->modem->path, path))
|
||||
continue;
|
||||
|
||||
at_network_registration_exit(at->modem);
|
||||
at_voicecall_exit(at->modem);
|
||||
ofono_modem_unregister(at->modem);
|
||||
|
||||
g_sessions = g_slist_remove(g_sessions, at);
|
||||
at_destroy(at);
|
||||
|
||||
modem_list(&modems);
|
||||
dbus_gsm_signal_array_property_changed(conn, "/",
|
||||
AT_MANAGER_INTERFACE,
|
||||
"Modems", DBUS_TYPE_OBJECT_PATH,
|
||||
&modems);
|
||||
g_free(modems);
|
||||
|
||||
return dbus_message_new_method_return(msg);
|
||||
}
|
||||
|
||||
return dbus_gsm_not_found(msg);
|
||||
}
|
||||
|
||||
static DBusMessage *manager_get_properties(DBusConnection *conn,
|
||||
DBusMessage *msg, void *data)
|
||||
{
|
||||
DBusMessageIter iter;
|
||||
DBusMessageIter dict;
|
||||
DBusMessage *reply;
|
||||
char **modems;
|
||||
|
||||
reply = dbus_message_new_method_return(msg);
|
||||
|
||||
if (!reply)
|
||||
return NULL;
|
||||
|
||||
modem_list(&modems);
|
||||
|
||||
dbus_message_iter_init_append(reply, &iter);
|
||||
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
|
||||
PROPERTIES_ARRAY_SIGNATURE, &dict);
|
||||
|
||||
dbus_gsm_dict_append_array(&dict, "Modems", DBUS_TYPE_OBJECT_PATH,
|
||||
&modems);
|
||||
|
||||
g_free(modems);
|
||||
|
||||
dbus_message_iter_close_container(&iter, &dict);
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
static GDBusMethodTable manager_methods[] = {
|
||||
{ "Create", "ss", "o", manager_create,
|
||||
G_DBUS_METHOD_FLAG_ASYNC },
|
||||
{ "Destroy", "o", "", manager_destroy },
|
||||
{ "GetProperties", "", "a{sv}", manager_get_properties },
|
||||
{ }
|
||||
};
|
||||
|
||||
static GDBusSignalTable manager_signals[] = {
|
||||
{ "PropertyChanged", "sv" },
|
||||
{ }
|
||||
};
|
||||
|
||||
static int manager_init(DBusConnection *conn)
|
||||
{
|
||||
if (g_dbus_register_interface(conn, "/", AT_MANAGER_INTERFACE,
|
||||
manager_methods, manager_signals,
|
||||
NULL, NULL, manager_free) == FALSE)
|
||||
return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void manager_exit(DBusConnection *conn)
|
||||
{
|
||||
g_dbus_unregister_interface(conn, "/", AT_MANAGER_INTERFACE);
|
||||
}
|
||||
|
||||
static int atmodem_init(void)
|
||||
{
|
||||
DBG("");
|
||||
DBusConnection *conn = dbus_gsm_connection();
|
||||
|
||||
manager_init(conn);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void atmodem_exit(void)
|
||||
{
|
||||
DBG("");
|
||||
DBusConnection *conn = dbus_gsm_connection();
|
||||
|
||||
manager_exit(conn);
|
||||
}
|
||||
|
||||
OFONO_PLUGIN_DEFINE(atmodem, "AT modem driver", VERSION,
|
||||
|
|
|
@ -0,0 +1,675 @@
|
|||
/*
|
||||
*
|
||||
* 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 "driver.h"
|
||||
|
||||
#include "gatchat.h"
|
||||
#include "gatresult.h"
|
||||
|
||||
#include "at.h"
|
||||
|
||||
static const char *none_prefix[] = { NULL };
|
||||
static const char *creg_prefix[] = { "+CREG:", NULL };
|
||||
static const char *cops_prefix[] = { "+COPS:", NULL };
|
||||
static const char *csq_prefix[] = { "+CSQ:", NULL };
|
||||
|
||||
struct netreg_data {
|
||||
gboolean supports_tech;
|
||||
short mnc;
|
||||
short mcc;
|
||||
};
|
||||
|
||||
static void extract_mcc_mnc(const char *str, short *mcc, short *mnc)
|
||||
{
|
||||
int num = 0;
|
||||
unsigned int i;
|
||||
|
||||
/* Three digit country code */
|
||||
for (i = 0; i < 3; i++)
|
||||
num = num * 10 + (int)(str[i] - '0');
|
||||
|
||||
*mcc = num;
|
||||
|
||||
num = 0;
|
||||
|
||||
/* Usually a 2 but sometimes 3 digit network code */
|
||||
for (; i < strlen(str); i++)
|
||||
num = num * 10 + (int)(str[i] - '0');
|
||||
|
||||
*mnc = num;
|
||||
}
|
||||
|
||||
static void at_creg_cb(gboolean ok, GAtResult *result, gpointer user_data)
|
||||
{
|
||||
struct cb_data *cbd = user_data;
|
||||
struct at_data *at = ofono_modem_userdata(cbd->modem);
|
||||
GAtResultIter iter;
|
||||
ofono_registration_status_cb_t cb = cbd->cb;
|
||||
int status;
|
||||
const char *str;
|
||||
int lac = -1, ci = -1, tech = -1;
|
||||
struct ofono_error error;
|
||||
|
||||
dump_response("at_creg_cb", ok, result);
|
||||
decode_at_error(&error, g_at_result_final_response(result));
|
||||
|
||||
if (!ok) {
|
||||
cb(&error, -1, -1, -1, -1, cbd->data);
|
||||
return;
|
||||
}
|
||||
|
||||
g_at_result_iter_init(&iter, result);
|
||||
|
||||
if (!g_at_result_iter_next(&iter, "+CREG:")) {
|
||||
DECLARE_FAILURE(e);
|
||||
|
||||
cb(&e, -1, -1, -1, -1, cbd->data);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Skip <n> the unsolicited result code */
|
||||
g_at_result_iter_skip_next(&iter);
|
||||
|
||||
g_at_result_iter_next_number(&iter, &status);
|
||||
|
||||
if (g_at_result_iter_next_string(&iter, &str) == TRUE)
|
||||
lac = strtol(str, NULL, 16);
|
||||
|
||||
if (g_at_result_iter_next_string(&iter, &str) == TRUE)
|
||||
ci = strtol(str, NULL, 16);
|
||||
|
||||
if (g_at_result_iter_next_number(&iter, &tech) == TRUE)
|
||||
at->netreg->supports_tech = TRUE;
|
||||
|
||||
ofono_debug("creg_cb: %d, %d, %d, %d", status, lac, ci, tech);
|
||||
|
||||
cb(&error, status, lac, ci, tech, cbd->data);
|
||||
}
|
||||
|
||||
static void at_registration_status(struct ofono_modem *modem,
|
||||
ofono_registration_status_cb_t cb,
|
||||
void *data)
|
||||
{
|
||||
struct at_data *at = ofono_modem_userdata(modem);
|
||||
struct cb_data *cbd = cb_data_new(modem, cb, data);
|
||||
|
||||
if (!cbd)
|
||||
goto error;
|
||||
|
||||
if (g_at_chat_send(at->parser, "AT+CREG?", creg_prefix,
|
||||
at_creg_cb, cbd, g_free) > 0)
|
||||
return;
|
||||
|
||||
error:
|
||||
if (cbd)
|
||||
g_free(cbd);
|
||||
|
||||
{
|
||||
DECLARE_FAILURE(error);
|
||||
cb(&error, -1, -1, -1, -1, data);
|
||||
}
|
||||
}
|
||||
|
||||
static void cops_cb(gboolean ok, GAtResult *result, gpointer user_data)
|
||||
{
|
||||
struct cb_data *cbd = user_data;
|
||||
struct at_data *at = ofono_modem_userdata(cbd->modem);
|
||||
ofono_current_operator_cb_t cb = cbd->cb;
|
||||
struct ofono_network_operator op;
|
||||
GAtResultIter iter;
|
||||
int format, tech;
|
||||
const char *name;
|
||||
struct ofono_error error;
|
||||
|
||||
dump_response("cops_cb", ok, result);
|
||||
decode_at_error(&error, g_at_result_final_response(result));
|
||||
|
||||
if (!ok || at->netreg->mcc == -1 || at->netreg->mnc == -1) {
|
||||
cb(&error, NULL, cbd->data);
|
||||
goto out;
|
||||
}
|
||||
|
||||
g_at_result_iter_init(&iter, result);
|
||||
|
||||
if (!g_at_result_iter_next(&iter, "+COPS:"))
|
||||
goto error;
|
||||
|
||||
g_at_result_iter_skip_next(&iter);
|
||||
|
||||
ok = g_at_result_iter_next_number(&iter, &format);
|
||||
|
||||
if (ok == FALSE || format != 0)
|
||||
goto error;
|
||||
|
||||
if (g_at_result_iter_next_string(&iter, &name) == FALSE)
|
||||
goto error;
|
||||
|
||||
/* Default to GSM */
|
||||
if (g_at_result_iter_next_number(&iter, &tech) == FALSE)
|
||||
tech = 0;
|
||||
|
||||
strncpy(op.name, name, OFONO_MAX_OPERATOR_NAME_LENGTH);
|
||||
op.name[OFONO_MAX_OPERATOR_NAME_LENGTH] = '\0';
|
||||
|
||||
op.mcc = at->netreg->mcc;
|
||||
op.mnc = at->netreg->mnc;
|
||||
op.status = -1;
|
||||
op.tech = tech;
|
||||
|
||||
ofono_debug("cops_cb: %s, %hd %hd %d", name, at->netreg->mcc,
|
||||
at->netreg->mnc, tech);
|
||||
|
||||
cb(&error, &op, cbd->data);
|
||||
|
||||
out:
|
||||
g_free(cbd);
|
||||
|
||||
return;
|
||||
|
||||
error:
|
||||
{
|
||||
DECLARE_FAILURE(e);
|
||||
|
||||
cb(&e, NULL, cbd->data);
|
||||
}
|
||||
|
||||
g_free(cbd);
|
||||
}
|
||||
|
||||
static void cops_numeric_cb(gboolean ok, GAtResult *result, gpointer user_data)
|
||||
{
|
||||
struct cb_data *cbd = user_data;
|
||||
struct at_data *at = ofono_modem_userdata(cbd->modem);
|
||||
GAtResultIter iter;
|
||||
const char *str;
|
||||
int format;
|
||||
|
||||
dump_response("cops_numeric_cb", ok, result);
|
||||
|
||||
if (!ok)
|
||||
goto error;
|
||||
|
||||
g_at_result_iter_init(&iter, result);
|
||||
|
||||
if (!g_at_result_iter_next(&iter, "+COPS:"))
|
||||
goto error;
|
||||
|
||||
g_at_result_iter_skip_next(&iter);
|
||||
|
||||
ok = g_at_result_iter_next_number(&iter, &format);
|
||||
|
||||
if (ok == FALSE || format != 2)
|
||||
goto error;
|
||||
|
||||
if (g_at_result_iter_next_string(&iter, &str) == FALSE ||
|
||||
strlen(str) == 0)
|
||||
goto error;
|
||||
|
||||
extract_mcc_mnc(str, &at->netreg->mcc, &at->netreg->mnc);
|
||||
|
||||
ofono_debug("Cops numeric got mcc: %hd, mnc: %hd",
|
||||
at->netreg->mcc, at->netreg->mnc);
|
||||
|
||||
return;
|
||||
|
||||
error:
|
||||
at->netreg->mcc = at->netreg->mnc = -1;
|
||||
}
|
||||
|
||||
static void at_current_operator(struct ofono_modem *modem,
|
||||
ofono_current_operator_cb_t cb, void *data)
|
||||
{
|
||||
struct at_data *at = ofono_modem_userdata(modem);
|
||||
struct cb_data *cbd = cb_data_new(modem, cb, data);
|
||||
gboolean ok;
|
||||
|
||||
if (!cbd)
|
||||
goto error;
|
||||
|
||||
ok = g_at_chat_send(at->parser, "AT+COPS=3,2", none_prefix,
|
||||
NULL, NULL, NULL);
|
||||
|
||||
if (ok)
|
||||
ok = g_at_chat_send(at->parser, "AT+COPS?", cops_prefix,
|
||||
cops_numeric_cb, cbd, NULL);
|
||||
|
||||
if (ok)
|
||||
ok = g_at_chat_send(at->parser, "AT+COPS=3,0", none_prefix,
|
||||
NULL, NULL, NULL);
|
||||
|
||||
if (ok)
|
||||
ok = g_at_chat_send(at->parser, "AT+COPS?", cops_prefix,
|
||||
cops_cb, cbd, NULL);
|
||||
|
||||
if (ok)
|
||||
return;
|
||||
|
||||
error:
|
||||
if (cbd)
|
||||
g_free(cbd);
|
||||
|
||||
{
|
||||
DECLARE_FAILURE(error);
|
||||
cb(&error, NULL, data);
|
||||
}
|
||||
}
|
||||
|
||||
static void cops_list_cb(gboolean ok, GAtResult *result, gpointer user_data)
|
||||
{
|
||||
struct cb_data *cbd = user_data;
|
||||
//struct at_data *at = ofono_modem_userdata(cbd->modem);
|
||||
ofono_operator_list_cb_t cb = cbd->cb;
|
||||
struct ofono_network_operator *list;
|
||||
GAtResultIter iter;
|
||||
int num = 0;
|
||||
struct ofono_error error;
|
||||
|
||||
dump_response("cops_list_cb", ok, result);
|
||||
decode_at_error(&error, g_at_result_final_response(result));
|
||||
|
||||
if (!ok) {
|
||||
cb(&error, 0, NULL, cbd->data);
|
||||
return;
|
||||
}
|
||||
|
||||
g_at_result_iter_init(&iter, result);
|
||||
|
||||
while (g_at_result_iter_next(&iter, "+COPS:")) {
|
||||
while (g_at_result_iter_skip_next(&iter))
|
||||
num += 1;
|
||||
}
|
||||
|
||||
ofono_debug("Got %d elements", num);
|
||||
|
||||
list = g_try_new0(struct ofono_network_operator, num);
|
||||
|
||||
if (!list) {
|
||||
DECLARE_FAILURE(e);
|
||||
cb(&e, 0, NULL, cbd->data);
|
||||
return;
|
||||
}
|
||||
|
||||
num = 0;
|
||||
g_at_result_iter_init(&iter, result);
|
||||
|
||||
while (g_at_result_iter_next(&iter, "+COPS:")) {
|
||||
int status, tech;
|
||||
const char *l, *s, *n;
|
||||
gboolean have_long = FALSE;
|
||||
|
||||
while (1) {
|
||||
if (!g_at_result_iter_open_list(&iter))
|
||||
break;
|
||||
|
||||
if (!g_at_result_iter_next_number(&iter, &status))
|
||||
break;
|
||||
|
||||
list[num].status = status;
|
||||
|
||||
if (!g_at_result_iter_next_string(&iter, &l))
|
||||
break;
|
||||
|
||||
if (strlen(l) > 0) {
|
||||
have_long = TRUE;
|
||||
strncpy(list[num].name, l,
|
||||
OFONO_MAX_OPERATOR_NAME_LENGTH);
|
||||
}
|
||||
|
||||
if (!g_at_result_iter_next_string(&iter, &s))
|
||||
break;
|
||||
|
||||
if (strlen(s) > 0 && !have_long)
|
||||
strncpy(list[num].name, s,
|
||||
OFONO_MAX_OPERATOR_NAME_LENGTH);
|
||||
|
||||
list[num].name[OFONO_MAX_OPERATOR_NAME_LENGTH] = '\0';
|
||||
|
||||
if (!g_at_result_iter_next_string(&iter, &n))
|
||||
break;
|
||||
|
||||
extract_mcc_mnc(n, &list[num].mcc, &list[num].mnc);
|
||||
|
||||
if (!g_at_result_iter_next_number(&iter, &tech))
|
||||
tech = 0;
|
||||
|
||||
list[num].tech = tech;
|
||||
|
||||
if (!g_at_result_iter_close_list(&iter))
|
||||
break;
|
||||
|
||||
num += 1;
|
||||
}
|
||||
}
|
||||
|
||||
ofono_debug("Got %d operators", num);
|
||||
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (; i < num; i++) {
|
||||
ofono_debug("Operator: %s, %hd, %hd, status: %d, %d",
|
||||
list[i].name, list[i].mcc, list[i].mnc,
|
||||
list[i].status, list[i].tech);
|
||||
}
|
||||
}
|
||||
|
||||
cb(&error, num, list, cbd->data);
|
||||
|
||||
g_free(list);
|
||||
}
|
||||
|
||||
static void at_list_operators(struct ofono_modem *modem, ofono_operator_list_cb_t cb,
|
||||
void *data)
|
||||
{
|
||||
struct at_data *at = ofono_modem_userdata(modem);
|
||||
struct cb_data *cbd = cb_data_new(modem, cb, data);
|
||||
|
||||
if (!cbd)
|
||||
goto error;
|
||||
|
||||
if (g_at_chat_send(at->parser, "AT+COPS=?", cops_prefix,
|
||||
cops_list_cb, cbd, g_free) > 0)
|
||||
return;
|
||||
|
||||
error:
|
||||
if (cbd)
|
||||
g_free(cbd);
|
||||
|
||||
{
|
||||
DECLARE_FAILURE(error);
|
||||
cb(&error, 0, NULL, data);
|
||||
}
|
||||
}
|
||||
|
||||
static void register_cb(gboolean ok, GAtResult *result, gpointer user_data)
|
||||
{
|
||||
struct cb_data *cbd = user_data;
|
||||
ofono_generic_cb_t cb = cbd->cb;
|
||||
struct ofono_error error;
|
||||
|
||||
dump_response("register_cb", ok, result);
|
||||
decode_at_error(&error, g_at_result_final_response(result));
|
||||
|
||||
cb(&error, cbd->data);
|
||||
}
|
||||
|
||||
static void at_register_auto(struct ofono_modem *modem, ofono_generic_cb_t cb,
|
||||
void *data)
|
||||
{
|
||||
struct at_data *at = ofono_modem_userdata(modem);
|
||||
struct cb_data *cbd = cb_data_new(modem, cb, data);
|
||||
|
||||
if (!cbd)
|
||||
goto error;
|
||||
|
||||
if (g_at_chat_send(at->parser, "AT+COPS=0", none_prefix,
|
||||
register_cb, cbd, g_free) > 0)
|
||||
return;
|
||||
|
||||
error:
|
||||
if (cbd)
|
||||
g_free(cbd);
|
||||
|
||||
{
|
||||
DECLARE_FAILURE(error);
|
||||
cb(&error, data);
|
||||
}
|
||||
}
|
||||
|
||||
static void at_register_manual(struct ofono_modem *modem,
|
||||
const struct ofono_network_operator *oper,
|
||||
ofono_generic_cb_t cb, void *data)
|
||||
{
|
||||
struct at_data *at = ofono_modem_userdata(modem);
|
||||
struct cb_data *cbd = cb_data_new(modem, cb, data);
|
||||
char buf[128];
|
||||
|
||||
if (!cbd)
|
||||
goto error;
|
||||
|
||||
if (at->netreg->supports_tech && oper->tech != -1)
|
||||
sprintf(buf, "AT+COPS=1,2,\"%03hd%02hd\",%1d", oper->mcc,
|
||||
oper->mnc,
|
||||
oper->tech);
|
||||
else
|
||||
sprintf(buf, "AT+COPS=1,2,\"%03hd%02hd\"", oper->mcc,
|
||||
oper->mnc);
|
||||
|
||||
if (g_at_chat_send(at->parser, buf, none_prefix,
|
||||
register_cb, cbd, g_free) > 0)
|
||||
return;
|
||||
|
||||
error:
|
||||
if (cbd)
|
||||
g_free(cbd);
|
||||
|
||||
{
|
||||
DECLARE_FAILURE(error);
|
||||
cb(&error, data);
|
||||
}
|
||||
}
|
||||
|
||||
static void at_deregister(struct ofono_modem *modem, ofono_generic_cb_t cb,
|
||||
void *data)
|
||||
{
|
||||
struct at_data *at = ofono_modem_userdata(modem);
|
||||
struct cb_data *cbd = cb_data_new(modem, cb, data);
|
||||
|
||||
if (!cbd)
|
||||
goto error;
|
||||
|
||||
if (g_at_chat_send(at->parser, "AT+COPS=2", none_prefix,
|
||||
register_cb, cbd, g_free) > 0)
|
||||
return;
|
||||
|
||||
error:
|
||||
if (cbd)
|
||||
g_free(cbd);
|
||||
|
||||
{
|
||||
DECLARE_FAILURE(error);
|
||||
cb(&error, data);
|
||||
}
|
||||
}
|
||||
|
||||
static void csq_notify(GAtResult *result, gpointer user_data)
|
||||
{
|
||||
struct ofono_modem *modem = user_data;
|
||||
//struct at_data *at = ofono_modem_userdata(modem);
|
||||
int strength;
|
||||
GAtResultIter iter;
|
||||
|
||||
dump_response("csq_notify", TRUE, result);
|
||||
|
||||
g_at_result_iter_init(&iter, result);
|
||||
|
||||
if (!g_at_result_iter_next(&iter, "+CSQ:"))
|
||||
return;
|
||||
|
||||
if (!g_at_result_iter_next_number(&iter, &strength))
|
||||
return;
|
||||
|
||||
ofono_debug("csq_notify: %d", strength);
|
||||
|
||||
if (strength == 99)
|
||||
strength = -1;
|
||||
else
|
||||
strength = strength * 100 / 31;
|
||||
|
||||
ofono_signal_strength_notify(modem, strength);
|
||||
}
|
||||
|
||||
static void csq_cb(gboolean ok, GAtResult *result, gpointer user_data)
|
||||
{
|
||||
struct cb_data *cbd = user_data;
|
||||
ofono_signal_strength_cb_t cb = cbd->cb;
|
||||
int strength;
|
||||
GAtResultIter iter;
|
||||
struct ofono_error error;
|
||||
|
||||
dump_response("csq_cb", ok, result);
|
||||
decode_at_error(&error, g_at_result_final_response(result));
|
||||
|
||||
if (!ok) {
|
||||
cb(&error, -1, cbd->data);
|
||||
return;
|
||||
}
|
||||
|
||||
g_at_result_iter_init(&iter, result);
|
||||
|
||||
if (!g_at_result_iter_next(&iter, "+CSQ:")) {
|
||||
DECLARE_FAILURE(e);
|
||||
|
||||
cb(&e, -1, cbd->data);
|
||||
return;
|
||||
}
|
||||
|
||||
g_at_result_iter_next_number(&iter, &strength);
|
||||
|
||||
ofono_debug("csq_cb: %d", strength);
|
||||
|
||||
if (strength == 99)
|
||||
strength = -1;
|
||||
else
|
||||
strength = strength * 100 / 31;
|
||||
|
||||
cb(&error, strength, cbd->data);
|
||||
}
|
||||
|
||||
static void at_signal_strength(struct ofono_modem *modem,
|
||||
ofono_signal_strength_cb_t cb, void *data)
|
||||
{
|
||||
struct at_data *at = ofono_modem_userdata(modem);
|
||||
struct cb_data *cbd = cb_data_new(modem, cb, data);
|
||||
|
||||
if (!cbd)
|
||||
goto error;
|
||||
|
||||
if (g_at_chat_send(at->parser, "AT+CSQ", csq_prefix,
|
||||
csq_cb, cbd, g_free) > 0)
|
||||
return;
|
||||
|
||||
error:
|
||||
if (cbd)
|
||||
g_free(cbd);
|
||||
|
||||
{
|
||||
DECLARE_FAILURE(error);
|
||||
cb(&error, -1, data);
|
||||
}
|
||||
}
|
||||
|
||||
static void creg_notify(GAtResult *result, gpointer user_data)
|
||||
{
|
||||
struct ofono_modem *modem = user_data;
|
||||
struct at_data *at = ofono_modem_userdata(modem);
|
||||
GAtResultIter iter;
|
||||
int status;
|
||||
int lac = -1, ci = -1, tech = -1;
|
||||
const char *str;
|
||||
|
||||
dump_response("creg_notify", TRUE, result);
|
||||
|
||||
g_at_result_iter_init(&iter, result);
|
||||
|
||||
if (!g_at_result_iter_next(&iter, "+CREG:"))
|
||||
return;
|
||||
|
||||
g_at_result_iter_next_number(&iter, &status);
|
||||
|
||||
if (g_at_result_iter_next_string(&iter, &str) == TRUE)
|
||||
lac = strtol(str, NULL, 16);
|
||||
|
||||
if (g_at_result_iter_next_string(&iter, &str) == TRUE)
|
||||
ci = strtol(str, NULL, 16);
|
||||
|
||||
if (g_at_result_iter_next_number(&iter, &tech) == TRUE)
|
||||
at->netreg->supports_tech = TRUE;
|
||||
|
||||
ofono_debug("creg_notify: %d, %d, %d, %d", status, lac, ci, tech);
|
||||
|
||||
ofono_network_registration_notify(modem, status, lac, ci, tech);
|
||||
}
|
||||
|
||||
static struct ofono_network_registration_ops ops = {
|
||||
.registration_status = at_registration_status,
|
||||
.current_operator = at_current_operator,
|
||||
.list_operators = at_list_operators,
|
||||
.register_auto = at_register_auto,
|
||||
.register_manual = at_register_manual,
|
||||
.deregister = at_deregister,
|
||||
.signal_strength = at_signal_strength,
|
||||
};
|
||||
|
||||
static void at_network_registration_initialized(gboolean ok, GAtResult *result,
|
||||
gpointer user_data)
|
||||
{
|
||||
struct ofono_modem *modem = user_data;
|
||||
struct at_data *at = ofono_modem_userdata(modem);
|
||||
|
||||
if (!ok) {
|
||||
ofono_error("Unable to initialize Network Registration");
|
||||
return;
|
||||
}
|
||||
|
||||
g_at_chat_register(at->parser, "+CREG:",
|
||||
creg_notify, FALSE, modem, NULL);
|
||||
g_at_chat_register(at->parser, "+CSQ:",
|
||||
csq_notify, FALSE, modem, NULL);
|
||||
|
||||
ofono_network_registration_register(modem, &ops);
|
||||
}
|
||||
|
||||
void at_network_registration_init(struct ofono_modem *modem)
|
||||
{
|
||||
struct at_data *at = ofono_modem_userdata(modem);
|
||||
|
||||
at->netreg = g_try_new0(struct netreg_data, 1);
|
||||
|
||||
if (!at->netreg)
|
||||
return;
|
||||
|
||||
g_at_chat_send(at->parser, "AT+CREG=2", NULL,
|
||||
at_network_registration_initialized,
|
||||
modem, NULL);
|
||||
}
|
||||
|
||||
void at_network_registration_exit(struct ofono_modem *modem)
|
||||
{
|
||||
struct at_data *at = ofono_modem_userdata(modem);
|
||||
|
||||
g_free(at->netreg);
|
||||
at->netreg = NULL;
|
||||
|
||||
ofono_network_registration_unregister(modem);
|
||||
}
|
|
@ -0,0 +1,256 @@
|
|||
/*
|
||||
*
|
||||
* 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 <stdio.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include <termios.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include <ofono/log.h>
|
||||
|
||||
#include "session.h"
|
||||
|
||||
struct modem_session_callback {
|
||||
modem_session_callback_t callback;
|
||||
gpointer user_data;
|
||||
GDestroyNotify notify;
|
||||
guint timeout_watcher;
|
||||
GIOChannel *io;
|
||||
};
|
||||
|
||||
static void connect_destroy(gpointer user)
|
||||
{
|
||||
struct modem_session_callback *callback = user;
|
||||
|
||||
if (callback->notify)
|
||||
callback->notify(callback->user_data);
|
||||
|
||||
if (callback->timeout_watcher != 0)
|
||||
g_source_remove(callback->timeout_watcher);
|
||||
|
||||
g_free(callback);
|
||||
}
|
||||
|
||||
static gboolean connect_cb(GIOChannel *io, GIOCondition cond, gpointer user)
|
||||
{
|
||||
struct modem_session_callback *callback = user;
|
||||
int err = 0;
|
||||
gboolean success;
|
||||
|
||||
if (cond & G_IO_NVAL)
|
||||
return FALSE;
|
||||
|
||||
if (cond & G_IO_OUT) {
|
||||
int sock = g_io_channel_unix_get_fd(io);
|
||||
socklen_t len = sizeof(err);
|
||||
|
||||
if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len) < 0)
|
||||
err = errno;
|
||||
} else if (cond & (G_IO_HUP | G_IO_ERR))
|
||||
err = ECONNRESET;
|
||||
|
||||
success = !err;
|
||||
|
||||
callback->callback(io, success, callback->user_data);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean connect_timeout(gpointer user)
|
||||
{
|
||||
struct modem_session_callback *callback = user;
|
||||
|
||||
callback->callback(callback->io, FALSE, callback->user_data);
|
||||
|
||||
callback->timeout_watcher = 0;
|
||||
|
||||
g_io_channel_unref(callback->io);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static int tty_open(const char *tty, struct termios *ti)
|
||||
{
|
||||
int sk;
|
||||
|
||||
sk = open(tty, O_RDWR | O_NOCTTY);
|
||||
|
||||
if (sk < 0) {
|
||||
ofono_error("Can't open TTY %s: %s(%d)",
|
||||
tty, strerror(errno), errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ti && tcsetattr(sk, TCSANOW, ti) < 0) {
|
||||
ofono_error("Can't change serial settings: %s(%d)",
|
||||
strerror(errno), errno);
|
||||
close(sk);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return sk;
|
||||
}
|
||||
#endif
|
||||
|
||||
static GIOChannel *socket_common(int sk, struct sockaddr *addr,
|
||||
socklen_t addrlen)
|
||||
{
|
||||
GIOChannel *io = g_io_channel_unix_new(sk);
|
||||
|
||||
if (io == NULL) {
|
||||
close(sk);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
g_io_channel_set_close_on_unref(io, TRUE);
|
||||
|
||||
if (g_io_channel_set_flags(io, G_IO_FLAG_NONBLOCK,
|
||||
NULL) != G_IO_STATUS_NORMAL) {
|
||||
g_io_channel_unref(io);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (connect(sk, addr, addrlen) < 0) {
|
||||
if (errno != EAGAIN && errno != EINPROGRESS) {
|
||||
g_io_channel_unref(io);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
return io;
|
||||
}
|
||||
|
||||
static GIOChannel *unix_connect(const char *address)
|
||||
{
|
||||
struct sockaddr_un addr;
|
||||
int sk;
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sun_family = PF_UNIX;
|
||||
|
||||
if (strncmp("x00", address, 3) == 0)
|
||||
strcpy(addr.sun_path + 1, address + 3);
|
||||
else
|
||||
strcpy(addr.sun_path, address);
|
||||
|
||||
sk = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
|
||||
if (sk < 0)
|
||||
return NULL;
|
||||
|
||||
return socket_common(sk, (struct sockaddr *)&addr, sizeof(addr));
|
||||
}
|
||||
|
||||
static GIOChannel *tcp_connect(const char *address)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
int sk;
|
||||
unsigned short port;
|
||||
in_addr_t inetaddr;
|
||||
char *portstr;
|
||||
char addrstr[16];
|
||||
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
|
||||
portstr = strchr(address, ':');
|
||||
|
||||
if (!portstr || (unsigned int)(portstr-address) > (sizeof(addrstr) - 1))
|
||||
return NULL;
|
||||
|
||||
strncpy(addrstr, address, portstr-address);
|
||||
addrstr[portstr-address] = '\0';
|
||||
|
||||
portstr += 1;
|
||||
|
||||
port = atoi(portstr);
|
||||
|
||||
if (port == 0)
|
||||
return NULL;
|
||||
|
||||
inetaddr = inet_addr(addrstr);
|
||||
|
||||
if (inetaddr == INADDR_NONE)
|
||||
return NULL;
|
||||
|
||||
sk = socket(PF_INET, SOCK_STREAM, 0);
|
||||
|
||||
if (sk < 0)
|
||||
return NULL;
|
||||
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_addr.s_addr = inetaddr;
|
||||
addr.sin_port = htons(port);
|
||||
|
||||
return socket_common(sk, (struct sockaddr *) &addr, sizeof(addr));
|
||||
}
|
||||
|
||||
GIOChannel *modem_session_create(const char *target,
|
||||
modem_session_callback_t func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify notify)
|
||||
{
|
||||
struct modem_session_callback *callback;
|
||||
GIOChannel *io = NULL;
|
||||
GIOCondition cond;
|
||||
|
||||
if (target == NULL || func == NULL)
|
||||
return NULL;
|
||||
|
||||
if (!strncasecmp(target, "tcp:", 4))
|
||||
io = tcp_connect(target+4);
|
||||
else if (!strncasecmp(target, "unix:", 5))
|
||||
io = unix_connect(target+5);
|
||||
|
||||
if (io == NULL)
|
||||
return NULL;
|
||||
|
||||
callback = g_new0(struct modem_session_callback, 1);
|
||||
|
||||
callback->callback = func;
|
||||
callback->user_data = user_data;
|
||||
callback->notify = notify;
|
||||
callback->io = io;
|
||||
callback->timeout_watcher = g_timeout_add_seconds(20, connect_timeout,
|
||||
callback);
|
||||
|
||||
cond = G_IO_OUT | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
|
||||
g_io_add_watch_full(io, G_PRIORITY_DEFAULT, cond, connect_cb,
|
||||
callback, connect_destroy);
|
||||
|
||||
g_io_channel_unref(io);
|
||||
|
||||
return io;
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
*
|
||||
* 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
|
||||
*
|
||||
*/
|
||||
|
||||
typedef void (*modem_session_callback_t)(GIOChannel *io, gboolean success,
|
||||
gpointer user_data);
|
||||
|
||||
GIOChannel *modem_session_create(const char *target,
|
||||
modem_session_callback_t func,
|
||||
gpointer user_data,
|
||||
GDestroyNotify notify);
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
*
|
||||
* 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 "driver.h"
|
||||
#include "util.h"
|
||||
|
||||
#include "gatchat.h"
|
||||
#include "gatresult.h"
|
||||
|
||||
#include "at.h"
|
||||
|
||||
static const char *none_prefix[] = { NULL };
|
||||
|
||||
static void cusd_request_cb(gboolean ok, GAtResult *result, gpointer user_data)
|
||||
{
|
||||
struct cb_data *cbd = user_data;
|
||||
ofono_generic_cb_t cb = cbd->cb;
|
||||
struct ofono_error error;
|
||||
|
||||
dump_response("cusd_request_cb", ok, result);
|
||||
decode_at_error(&error, g_at_result_final_response(result));
|
||||
|
||||
cb(&error, cbd->data);
|
||||
}
|
||||
|
||||
static void at_ussd_request(struct ofono_modem *modem, const char *str,
|
||||
ofono_generic_cb_t cb, void *data)
|
||||
{
|
||||
struct at_data *at = ofono_modem_userdata(modem);
|
||||
struct cb_data *cbd = cb_data_new(modem, cb, data);
|
||||
unsigned char *converted;
|
||||
//struct ofono_error error;
|
||||
int dcs;
|
||||
int max_len;
|
||||
long written;
|
||||
char buf[256];
|
||||
|
||||
if (!cbd)
|
||||
goto error;
|
||||
|
||||
converted = convert_utf8_to_gsm(str, strlen(str), NULL, &written, 0);
|
||||
|
||||
/* TODO: Be able to convert to UCS2, although the standard does not
|
||||
* indicate that this is actually possible
|
||||
*/
|
||||
if (!converted)
|
||||
goto error;
|
||||
else {
|
||||
dcs = 15;
|
||||
max_len = 182;
|
||||
}
|
||||
|
||||
if (written > max_len)
|
||||
goto error;
|
||||
|
||||
sprintf(buf, "AT+CUSD=1,\"%s\",%d", converted, dcs);
|
||||
|
||||
if (g_at_chat_send(at->parser, buf, none_prefix,
|
||||
cusd_request_cb, cbd, g_free) > 0)
|
||||
return;
|
||||
|
||||
error:
|
||||
if (cbd)
|
||||
g_free(cbd);
|
||||
|
||||
{
|
||||
DECLARE_FAILURE(error);
|
||||
cb(&error, data);
|
||||
}
|
||||
}
|
||||
|
||||
static void cusd_cancel_cb(gboolean ok, GAtResult *result, gpointer user_data)
|
||||
{
|
||||
struct cb_data *cbd = user_data;
|
||||
ofono_generic_cb_t cb = cbd->cb;
|
||||
struct ofono_error error;
|
||||
|
||||
dump_response("cusd_cancel_cb", ok, result);
|
||||
decode_at_error(&error, g_at_result_final_response(result));
|
||||
|
||||
cb(&error, cbd->data);
|
||||
}
|
||||
|
||||
static void at_ussd_cancel(struct ofono_modem *modem,
|
||||
ofono_generic_cb_t cb, void *data)
|
||||
{
|
||||
struct at_data *at = ofono_modem_userdata(modem);
|
||||
struct cb_data *cbd = cb_data_new(modem, cb, data);
|
||||
|
||||
if (!cbd)
|
||||
goto error;
|
||||
|
||||
if (g_at_chat_send(at->parser, "AT+CUSD=2", none_prefix,
|
||||
cusd_cancel_cb, cbd, g_free) > 0)
|
||||
return;
|
||||
|
||||
error:
|
||||
if (cbd)
|
||||
g_free(cbd);
|
||||
|
||||
{
|
||||
DECLARE_FAILURE(error);
|
||||
cb(&error, data);
|
||||
}
|
||||
}
|
||||
|
||||
static struct ofono_ussd_ops ops = {
|
||||
.request = at_ussd_request,
|
||||
.cancel = at_ussd_cancel
|
||||
};
|
||||
|
||||
void at_ussd_init(struct ofono_modem *modem)
|
||||
{
|
||||
/* TODO: Register for USSD Notifications */
|
||||
ofono_ussd_register(modem, &ops);
|
||||
}
|
||||
|
||||
void at_ussd_exit(struct ofono_modem *modem)
|
||||
{
|
||||
ofono_ussd_unregister(modem);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue