Add initial implementation of AT modem driver

This commit is contained in:
Denis Kenzior 2009-05-10 23:39:27 -07:00 committed by Marcel Holtmann
parent 4f54515209
commit 6d486b7fe1
12 changed files with 3815 additions and 6 deletions

View File

@ -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)

89
drivers/atmodem/at.h Normal file
View File

@ -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);

View File

@ -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);
}

View File

@ -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, &currency);
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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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,

View File

@ -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);
}

256
drivers/atmodem/session.c Normal file
View File

@ -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;
}

28
drivers/atmodem/session.h Normal file
View File

@ -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);

151
drivers/atmodem/ussd.c Normal file
View File

@ -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);
}

1048
drivers/atmodem/voicecall.c Normal file

File diff suppressed because it is too large Load Diff