* RE: [PATCH 2/2] Telit HE910 general fixes for gprs, sms and voicecall
2015-03-06 16:07 [PATCH 2/2] Telit HE910 general fixes for gprs, sms and voicecall Enrico Sau
@ 2015-03-09 9:02 ` Kallumari
2015-03-09 12:21 ` Kallumari
2015-03-10 1:50 ` Denis Kenzior
2 siblings, 0 replies; 5+ messages in thread
From: Kallumari @ 2015-03-09 9:02 UTC (permalink / raw)
To: ofono
[-- Attachment #1: Type: text/plain, Size: 4182 bytes --]
Enrico/Denis,
I did all the changes required inside the respective sources as per Enrico’s patch 1/2 & 2/2.
I still have some issues. Not sure what is the exact reason for this. We tried checking if the sim itself is non-functional.
However, the sim seems to be working, we did a small test & we were able to ping using some scripts.
I have attached all the sources which I modified as required.
Let me know if I am doing anything wrong. On the device there is some process ‘pppd’ which was also looked to be using ttyACM0.
I tried stopping it though. Thinking that it could be blocking certain things.
Regards,
<Ram>
From: ofono [mailto:ofono-bounces(a)ofono.org] On Behalf Of Enrico Sau
Sent: Friday, March 06, 2015 9:38 PM
To: ofono(a)ofono.org
Subject: [PATCH 2/2] Telit HE910 general fixes for gprs, sms and voicecall
diff --git a/drivers/atmodem/sms.c b/drivers/atmodem/sms.c
index f93dd23..7aa90dc 100644
--- a/drivers/atmodem/sms.c
+++ b/drivers/atmodem/sms.c
@@ -396,7 +396,7 @@ static void at_cds_notify(GAtResult *result, gpointer user_data)
decode_hex_own_buf(hexpdu, -1, &pdu_len, 0, pdu);
ofono_sms_status_notify(sms, pdu, pdu_len, tpdu_len);
- if (data->cnma_enabled)
+ if (data->cnma_enabled && data->vendor != OFONO_VENDOR_TELIT)
at_ack_delivery(sms);
return;
@@ -429,7 +429,8 @@ static void at_cmt_notify(GAtResult *result, gpointer user_data)
decode_hex_own_buf(hexpdu, -1, &pdu_len, 0, pdu);
ofono_sms_deliver_notify(sms, pdu, pdu_len, tpdu_len);
- if (data->vendor != OFONO_VENDOR_SIMCOM)
+ if (data->vendor != OFONO_VENDOR_SIMCOM &&
+ data->vendor != OFONO_VENDOR_TELIT)
at_ack_delivery(sms);
}
diff --git a/drivers/atmodem/ussd.c b/drivers/atmodem/ussd.c
index 2e45317..7c6cd43 100644
--- a/drivers/atmodem/ussd.c
+++ b/drivers/atmodem/ussd.c
@@ -63,6 +63,18 @@ static void read_charset_cb(gboolean ok, GAtResult *result,
at_util_parse_cscs_query(result, &data->charset);
}
+static void charset_cb(gboolean ok, GAtResult *result,
+ gpointer user_data)
+{
+ struct ussd_data *data = user_data;
+
+ if (!ok)
+ return;
+
+ g_at_chat_send(data->chat, "AT+CSCS?", cscs_prefix,
+ read_charset_cb, data, NULL);
+}
+
static const unsigned char *ucs2_gsm_to_packed(const char *content,
long *msg_len,
unsigned char *msg)
@@ -308,8 +320,12 @@ static int at_ussd_probe(struct ofono_ussd *ussd, unsigned int vendor,
ofono_ussd_set_data(ussd, data);
- g_at_chat_send(data->chat, "AT+CSCS?", cscs_prefix,
- read_charset_cb, data, NULL);
+ if(vendor == OFONO_VENDOR_TELIT)
+ g_at_chat_send(data->chat, "AT+CSCS=GSM", cscs_prefix,
+ charset_cb, data, NULL);
+ else
+ g_at_chat_send(data->chat, "AT+CSCS?", cscs_prefix,
+ read_charset_cb, data, NULL);
g_at_chat_send(data->chat, "AT+CUSD=1", NULL,
at_ussd_register, ussd, NULL);
diff --git a/drivers/atmodem/voicecall.c b/drivers/atmodem/voicecall.c
index 7d823a2..7bfa65f 100644
--- a/drivers/atmodem/voicecall.c
+++ b/drivers/atmodem/voicecall.c
@@ -1116,8 +1116,12 @@ static int at_voicecall_probe(struct ofono_voicecall *vc, unsigned int vendor
g_at_chat_send(vd->chat, "AT+CRC=1", NULL, NULL, NULL, NULL);
g_at_chat_send(vd->chat, "AT+CLIP=1", NULL, NULL, NULL, NULL);
- g_at_chat_send(vd->chat, "AT+CDIP=1", NULL, NULL, NULL, NULL);
- g_at_chat_send(vd->chat, "AT+CNAP=1", NULL, NULL, NULL, NULL);
+
+ if (vd->vendor != OFONO_VENDOR_TELIT)
+ {
+ g_at_chat_send(vd->chat, "AT+CDIP=1", NULL, NULL, NULL, NULL);
+ g_at_chat_send(vd->chat, "AT+CNAP=1", NULL, NULL, NULL, NULL);
+ }
switch (vd->vendor) {
case OFONO_VENDOR_QUALCOMM_MSM:
[-- Attachment #2: attachment.html --]
[-- Type: text/html, Size: 19617 bytes --]
[-- Attachment #3: voicecall.c --]
[-- Type: text/plain, Size: 29691 bytes --]
/*
*
* oFono - Open Source Telephony
*
* Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#define _GNU_SOURCE
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <glib.h>
#include <ofono/log.h>
#include <ofono/modem.h>
#include <ofono/voicecall.h>
#include "vendor.h"
#include "gatchat.h"
#include "gatresult.h"
#include "common.h"
#include "atmodem.h"
/* Amount of ms we wait between CLCC calls */
#define POLL_CLCC_INTERVAL 500
/* Amount of time we give for CLIP to arrive before we commence CLCC poll */
#define CLIP_INTERVAL 200
/* When +VTD returns 0, an unspecified manufacturer-specific delay is used */
#define TONE_DURATION 1000
static const char *clcc_prefix[] = { "+CLCC:", NULL };
static const char *none_prefix[] = { NULL };
/* According to 27.007 COLP is an intermediate status for ATD */
static const char *atd_prefix[] = { "+COLP:", NULL };
#define FLAG_NEED_CLIP 1
#define FLAG_NEED_CNAP 2
#define FLAG_NEED_CDIP 4
struct voicecall_data {
GSList *calls;
unsigned int local_release;
unsigned int clcc_source;
GAtChat *chat;
unsigned int vendor;
unsigned int tone_duration;
guint vts_source;
unsigned int vts_delay;
unsigned char flags;
};
struct release_id_req {
struct ofono_voicecall *vc;
ofono_voicecall_cb_t cb;
void *data;
int id;
};
struct change_state_req {
struct ofono_voicecall *vc;
ofono_voicecall_cb_t cb;
void *data;
int affected_types;
};
static gboolean poll_clcc(gpointer user_data);
static int class_to_call_type(int cls)
{
switch (cls) {
case 1:
return 0;
case 4:
return 2;
case 8:
return 9;
default:
return 1;
}
}
static struct ofono_call *create_call(struct ofono_voicecall *vc, int type,
int direction, int status,
const char *num, int num_type, int clip)
{
struct voicecall_data *d = ofono_voicecall_get_data(vc);
struct ofono_call *call;
/* Generate a call structure for the waiting call */
call = g_try_new(struct ofono_call, 1);
if (call == NULL)
return NULL;
ofono_call_init(call);
call->id = ofono_voicecall_get_next_callid(vc);
call->type = type;
call->direction = direction;
call->status = status;
if (clip != 2) {
strncpy(call->phone_number.number, num,
OFONO_MAX_PHONE_NUMBER_LENGTH);
call->phone_number.type = num_type;
}
call->clip_validity = clip;
call->cnap_validity = CNAP_VALIDITY_NOT_AVAILABLE;
d->calls = g_slist_insert_sorted(d->calls, call, at_util_call_compare);
return call;
}
static void clcc_poll_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct ofono_voicecall *vc = user_data;
struct voicecall_data *vd = ofono_voicecall_get_data(vc);
GSList *calls;
GSList *n, *o;
struct ofono_call *nc, *oc;
gboolean poll_again = FALSE;
struct ofono_error error;
decode_at_error(&error, g_at_result_final_response(result));
if (!ok) {
/*
* On certain Option GTM modems CLCC polling can fail
* with a CME ERROR: 100. It seems to be safe to ignore
* it and continue polling anyway
*/
if (vd->vendor == OFONO_VENDOR_QUALCOMM_MSM &&
error.type == OFONO_ERROR_TYPE_CME &&
error.error == 100) {
poll_again = TRUE;
goto poll_again;
}
ofono_error("We are polling CLCC and received an error");
ofono_error("All bets are off for call management");
return;
}
calls = at_util_parse_clcc(result, NULL);
n = calls;
o = vd->calls;
while (n || o) {
nc = n ? n->data : NULL;
oc = o ? o->data : NULL;
switch (vd->vendor) {
case OFONO_VENDOR_QUALCOMM_MSM:
poll_again = TRUE;
break;
default:
if (nc && nc->status >= CALL_STATUS_DIALING &&
nc->status <= CALL_STATUS_WAITING)
poll_again = TRUE;
break;
}
if (oc && (nc == NULL || (nc->id > oc->id))) {
enum ofono_disconnect_reason reason;
if (vd->local_release & (1 << oc->id))
reason = OFONO_DISCONNECT_REASON_LOCAL_HANGUP;
else
reason = OFONO_DISCONNECT_REASON_REMOTE_HANGUP;
if (!oc->type)
ofono_voicecall_disconnected(vc, oc->id,
reason, NULL);
o = o->next;
} else if (nc && (oc == NULL || (nc->id < oc->id))) {
/* new call, signal it */
if (nc->type == 0)
ofono_voicecall_notify(vc, nc);
n = n->next;
} else {
/*
* Always use the clip_validity from old call
* the only place this is truly told to us is
* in the CLIP notify, the rest are fudged
* anyway. Useful when RING, CLIP is used,
* and we're forced to use CLCC and clip_validity
* is 1
*/
if (oc->clip_validity == 1)
nc->clip_validity = oc->clip_validity;
/*
* CNAP doesn't arrive as part of CLCC, always
* re-use from the old call
*/
strncpy(nc->name, oc->name,
OFONO_MAX_CALLER_NAME_LENGTH);
nc->name[OFONO_MAX_CALLER_NAME_LENGTH] = '\0';
nc->cnap_validity = oc->cnap_validity;
/*
* CDIP doesn't arrive as part of CLCC, always
* re-use from the old call
*/
memcpy(&nc->called_number, &oc->called_number,
sizeof(oc->called_number));
/*
* If the CLIP is not provided and the CLIP never
* arrives, or RING is used, then signal the call
* here
*/
if (nc->status == CALL_STATUS_INCOMING &&
(vd->flags & FLAG_NEED_CLIP)) {
if (nc->type == 0)
ofono_voicecall_notify(vc, nc);
vd->flags &= ~FLAG_NEED_CLIP;
} else if (memcmp(nc, oc, sizeof(*nc)) && nc->type == 0)
ofono_voicecall_notify(vc, nc);
n = n->next;
o = o->next;
}
}
g_slist_foreach(vd->calls, (GFunc) g_free, NULL);
g_slist_free(vd->calls);
vd->calls = calls;
vd->local_release = 0;
poll_again:
if (poll_again && !vd->clcc_source)
vd->clcc_source = g_timeout_add(POLL_CLCC_INTERVAL,
poll_clcc, vc);
}
static gboolean poll_clcc(gpointer user_data)
{
struct ofono_voicecall *vc = user_data;
struct voicecall_data *vd = ofono_voicecall_get_data(vc);
g_at_chat_send(vd->chat, "AT+CLCC", clcc_prefix,
clcc_poll_cb, vc, NULL);
vd->clcc_source = 0;
return FALSE;
}
static void generic_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct change_state_req *req = user_data;
struct voicecall_data *vd = ofono_voicecall_get_data(req->vc);
struct ofono_error error;
decode_at_error(&error, g_at_result_final_response(result));
if (ok && req->affected_types) {
GSList *l;
struct ofono_call *call;
for (l = vd->calls; l; l = l->next) {
call = l->data;
if (req->affected_types & (1 << call->status))
vd->local_release |= (1 << call->id);
}
}
g_at_chat_send(vd->chat, "AT+CLCC", clcc_prefix,
clcc_poll_cb, req->vc, NULL);
/* We have to callback after we schedule a poll if required */
req->cb(&error, req->data);
}
static void release_id_cb(gboolean ok, GAtResult *result,
gpointer user_data)
{
struct release_id_req *req = user_data;
struct voicecall_data *vd = ofono_voicecall_get_data(req->vc);
struct ofono_error error;
decode_at_error(&error, g_at_result_final_response(result));
if (ok)
vd->local_release = 1 << req->id;
g_at_chat_send(vd->chat, "AT+CLCC", clcc_prefix,
clcc_poll_cb, req->vc, NULL);
/* We have to callback after we schedule a poll if required */
req->cb(&error, req->data);
}
static void atd_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct cb_data *cbd = user_data;
struct ofono_voicecall *vc = cbd->user;
struct voicecall_data *vd = ofono_voicecall_get_data(vc);
ofono_voicecall_cb_t cb = cbd->cb;
GAtResultIter iter;
const char *num;
int type = 128;
int validity = 2;
struct ofono_error error;
struct ofono_call *call;
GSList *l;
decode_at_error(&error, g_at_result_final_response(result));
if (!ok)
goto out;
/* On a success, make sure to put all active calls on hold */
for (l = vd->calls; l; l = l->next) {
call = l->data;
if (call->status != CALL_STATUS_ACTIVE)
continue;
call->status = CALL_STATUS_HELD;
ofono_voicecall_notify(vc, call);
}
g_at_result_iter_init(&iter, result);
if (g_at_result_iter_next(&iter, "+COLP:")) {
g_at_result_iter_next_string(&iter, &num);
g_at_result_iter_next_number(&iter, &type);
if (strlen(num) > 0)
validity = 0;
else
validity = 2;
DBG("colp_notify: %s %d %d", num, type, validity);
}
/* Generate a voice call that was just dialed, we guess the ID */
call = create_call(vc, 0, 0, CALL_STATUS_DIALING, num, type, validity);
if (call == NULL) {
ofono_error("Unable to malloc, call tracking will fail!");
return;
}
/* oFono core will generate a call with the dialed number
* inside its dial callback. Unless we got COLP information
* we do not need to communicate that a call is being
* dialed
*/
if (validity != 2)
ofono_voicecall_notify(vc, call);
if (!vd->clcc_source)
vd->clcc_source = g_timeout_add(POLL_CLCC_INTERVAL,
poll_clcc, vc);
out:
cb(&error, cbd->data);
}
static void at_dial(struct ofono_voicecall *vc,
const struct ofono_phone_number *ph,
enum ofono_clir_option clir, ofono_voicecall_cb_t cb,
void *data)
{
struct voicecall_data *vd = ofono_voicecall_get_data(vc);
struct cb_data *cbd = cb_data_new(cb, data);
char buf[256];
cbd->user = vc;
if (ph->type == 145)
snprintf(buf, sizeof(buf), "ATD+%s", ph->number);
else
snprintf(buf, sizeof(buf), "ATD%s", ph->number);
switch (clir) {
case OFONO_CLIR_OPTION_INVOCATION:
strcat(buf, "I");
break;
case OFONO_CLIR_OPTION_SUPPRESSION:
strcat(buf, "i");
break;
default:
break;
}
strcat(buf, ";");
if (g_at_chat_send(vd->chat, buf, atd_prefix,
atd_cb, cbd, g_free) > 0)
return;
g_free(cbd);
CALLBACK_WITH_FAILURE(cb, data);
}
static void at_template(const char *cmd, struct ofono_voicecall *vc,
GAtResultFunc result_cb, unsigned int affected_types,
ofono_voicecall_cb_t cb, void *data)
{
struct voicecall_data *vd = ofono_voicecall_get_data(vc);
struct change_state_req *req = g_try_new0(struct change_state_req, 1);
if (req == NULL)
goto error;
req->vc = vc;
req->cb = cb;
req->data = data;
req->affected_types = affected_types;
if (g_at_chat_send(vd->chat, cmd, none_prefix,
result_cb, req, g_free) > 0)
return;
error:
g_free(req);
CALLBACK_WITH_FAILURE(cb, data);
}
static void at_answer(struct ofono_voicecall *vc,
ofono_voicecall_cb_t cb, void *data)
{
at_template("ATA", vc, generic_cb, 0, cb, data);
}
static void at_hangup(struct ofono_voicecall *vc,
ofono_voicecall_cb_t cb, void *data)
{
/* Hangup active call */
at_template("AT+CHUP", vc, generic_cb, 0x3f, cb, data);
}
static void clcc_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct ofono_voicecall *vc = user_data;
struct voicecall_data *vd = ofono_voicecall_get_data(vc);
GSList *l;
if (!ok)
return;
vd->calls = at_util_parse_clcc(result, NULL);
for (l = vd->calls; l; l = l->next)
ofono_voicecall_notify(vc, l->data);
}
static void at_hold_all_active(struct ofono_voicecall *vc,
ofono_voicecall_cb_t cb, void *data)
{
at_template("AT+CHLD=2", vc, generic_cb, 0, cb, data);
}
static void at_release_all_held(struct ofono_voicecall *vc,
ofono_voicecall_cb_t cb, void *data)
{
unsigned int held_status = 1 << CALL_STATUS_HELD;
at_template("AT+CHLD=0", vc, generic_cb, held_status, cb, data);
}
static void at_set_udub(struct ofono_voicecall *vc,
ofono_voicecall_cb_t cb, void *data)
{
unsigned int incoming_or_waiting =
(1 << CALL_STATUS_INCOMING) | (1 << CALL_STATUS_WAITING);
at_template("AT+CHLD=0", vc, generic_cb, incoming_or_waiting,
cb, data);
}
static void at_release_all_active(struct ofono_voicecall *vc,
ofono_voicecall_cb_t cb, void *data)
{
at_template("AT+CHLD=1", vc, generic_cb, 0x1, cb, data);
}
static void at_release_specific(struct ofono_voicecall *vc, int id,
ofono_voicecall_cb_t cb, void *data)
{
struct voicecall_data *vd = ofono_voicecall_get_data(vc);
struct release_id_req *req = g_try_new0(struct release_id_req, 1);
char buf[32];
if (req == NULL)
goto error;
req->vc = vc;
req->cb = cb;
req->data = data;
req->id = id;
snprintf(buf, sizeof(buf), "AT+CHLD=1%d", id);
if (g_at_chat_send(vd->chat, buf, none_prefix,
release_id_cb, req, g_free) > 0)
return;
error:
g_free(req);
CALLBACK_WITH_FAILURE(cb, data);
}
static void at_private_chat(struct ofono_voicecall *vc, int id,
ofono_voicecall_cb_t cb, void *data)
{
char buf[32];
snprintf(buf, sizeof(buf), "AT+CHLD=2%d", id);
at_template(buf, vc, generic_cb, 0, cb, data);
}
static void at_create_multiparty(struct ofono_voicecall *vc,
ofono_voicecall_cb_t cb, void *data)
{
at_template("AT+CHLD=3", vc, generic_cb, 0, cb, data);
}
static void at_transfer(struct ofono_voicecall *vc,
ofono_voicecall_cb_t cb, void *data)
{
/* Held & Active */
unsigned int transfer = 0x1 | 0x2;
/* Transfer can puts held & active calls together and disconnects
* from both. However, some networks support transferring of
* dialing/ringing calls as well.
*/
transfer |= 0x4 | 0x8;
at_template("AT+CHLD=4", vc, generic_cb, transfer, cb, data);
}
static void at_deflect(struct ofono_voicecall *vc,
const struct ofono_phone_number *ph,
ofono_voicecall_cb_t cb, void *data)
{
char buf[128];
unsigned int incoming_or_waiting =
(1 << CALL_STATUS_INCOMING) | (1 << CALL_STATUS_WAITING);
snprintf(buf, sizeof(buf), "AT+CTFR=%s,%d", ph->number, ph->type);
at_template(buf, vc, generic_cb, incoming_or_waiting, cb, data);
}
static gboolean vts_timeout_cb(gpointer user_data)
{
struct cb_data *cbd = user_data;
struct voicecall_data *vd = cbd->user;
ofono_voicecall_cb_t cb = cbd->cb;
vd->vts_source = 0;
CALLBACK_WITH_SUCCESS(cb, cbd->data);
g_free(cbd);
return FALSE;
}
static void vts_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct cb_data *cbd = user_data;
struct voicecall_data *vd = cbd->user;
ofono_voicecall_cb_t cb = cbd->cb;
struct ofono_error error;
decode_at_error(&error, g_at_result_final_response(result));
if (!ok) {
cb(&error, cbd->data);
g_free(cbd);
return;
}
vd->vts_source = g_timeout_add(vd->vts_delay, vts_timeout_cb, cbd);
}
static void at_send_dtmf(struct ofono_voicecall *vc, const char *dtmf,
ofono_voicecall_cb_t cb, void *data)
{
struct voicecall_data *vd = ofono_voicecall_get_data(vc);
struct cb_data *cbd = cb_data_new(cb, data);
int len = strlen(dtmf);
int s;
int i;
char *buf;
cbd->user = vd;
/* strlen("+VTS=T;") = 7 + initial AT + null */
buf = g_try_new(char, len * 9 + 3);
if (buf == NULL)
goto error;
s = sprintf(buf, "AT+VTS=%c", dtmf[0]);
for (i = 1; i < len; i++)
s += sprintf(buf + s, ";+VTS=%c", dtmf[i]);
vd->vts_delay = vd->tone_duration * len;
s = g_at_chat_send(vd->chat, buf, none_prefix,
vts_cb, cbd, NULL);
g_free(buf);
if (s > 0)
return;
error:
g_free(cbd);
CALLBACK_WITH_FAILURE(cb, data);
}
static void ring_notify(GAtResult *result, gpointer user_data)
{
struct ofono_voicecall *vc = user_data;
struct voicecall_data *vd = ofono_voicecall_get_data(vc);
struct ofono_call *call;
/* See comment in CRING */
if (g_slist_find_custom(vd->calls,
GINT_TO_POINTER(CALL_STATUS_WAITING),
at_util_call_compare_by_status))
return;
/* RING can repeat, ignore if we already have an incoming call */
if (g_slist_find_custom(vd->calls,
GINT_TO_POINTER(CALL_STATUS_INCOMING),
at_util_call_compare_by_status))
return;
/* Generate an incoming call of unknown type */
call = create_call(vc, 9, 1, CALL_STATUS_INCOMING, NULL, 128, 2);
if (call == NULL) {
ofono_error("Couldn't create call, call management is fubar!");
return;
}
/* We don't know the call type, we must run clcc */
vd->clcc_source = g_timeout_add(CLIP_INTERVAL, poll_clcc, vc);
vd->flags = FLAG_NEED_CLIP | FLAG_NEED_CNAP | FLAG_NEED_CDIP;
}
static void cring_notify(GAtResult *result, gpointer user_data)
{
struct ofono_voicecall *vc = user_data;
struct voicecall_data *vd = ofono_voicecall_get_data(vc);
GAtResultIter iter;
const char *line;
int type;
/* Handle the following situation:
* Active Call + Waiting Call. Active Call is Released. The Waiting
* call becomes Incoming and RING/CRING indications are signaled.
* Sometimes these arrive before we managed to poll CLCC to find about
* the stage change. If this happens, simply ignore the RING/CRING
* when a waiting call exists (cannot have waiting + incoming in GSM)
*/
if (g_slist_find_custom(vd->calls,
GINT_TO_POINTER(CALL_STATUS_WAITING),
at_util_call_compare_by_status))
return;
/* CRING can repeat, ignore if we already have an incoming call */
if (g_slist_find_custom(vd->calls,
GINT_TO_POINTER(CALL_STATUS_INCOMING),
at_util_call_compare_by_status))
return;
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "+CRING:"))
return;
line = g_at_result_iter_raw_line(&iter);
if (line == NULL)
return;
/* Ignore everything that is not voice for now */
if (!strcasecmp(line, "VOICE"))
type = 0;
else
type = 9;
/* Generate an incoming call */
create_call(vc, type, 1, CALL_STATUS_INCOMING, NULL, 128, 2);
/* We have a call, and call type but don't know the number and
* must wait for the CLIP to arrive before announcing the call.
* So we wait, and schedule the clcc call. If the CLIP arrives
* earlier, we announce the call there
*/
vd->clcc_source = g_timeout_add(CLIP_INTERVAL, poll_clcc, vc);
vd->flags = FLAG_NEED_CLIP | FLAG_NEED_CNAP | FLAG_NEED_CDIP;
DBG("");
}
static void clip_notify(GAtResult *result, gpointer user_data)
{
struct ofono_voicecall *vc = user_data;
struct voicecall_data *vd = ofono_voicecall_get_data(vc);
GAtResultIter iter;
const char *num;
int type, validity;
GSList *l;
struct ofono_call *call;
l = g_slist_find_custom(vd->calls,
GINT_TO_POINTER(CALL_STATUS_INCOMING),
at_util_call_compare_by_status);
if (l == NULL) {
ofono_error("CLIP for unknown call");
return;
}
/* We have already saw a CLIP for this call, no need to parse again */
if ((vd->flags & FLAG_NEED_CLIP) == 0)
return;
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "+CLIP:"))
return;
if (!g_at_result_iter_next_string(&iter, &num))
return;
if (!g_at_result_iter_next_number(&iter, &type))
return;
if (strlen(num) > 0)
validity = CLIP_VALIDITY_VALID;
else
validity = CLIP_VALIDITY_NOT_AVAILABLE;
/* Skip subaddr, satype and alpha */
g_at_result_iter_skip_next(&iter);
g_at_result_iter_skip_next(&iter);
g_at_result_iter_skip_next(&iter);
/* If we have CLI validity field, override our guessed value */
g_at_result_iter_next_number(&iter, &validity);
DBG("%s %d %d", num, type, validity);
call = l->data;
strncpy(call->phone_number.number, num,
OFONO_MAX_PHONE_NUMBER_LENGTH);
call->phone_number.number[OFONO_MAX_PHONE_NUMBER_LENGTH] = '\0';
call->phone_number.type = type;
call->clip_validity = validity;
if (call->type == 0)
ofono_voicecall_notify(vc, call);
vd->flags &= ~FLAG_NEED_CLIP;
}
static void cdip_notify(GAtResult *result, gpointer user_data)
{
struct ofono_voicecall *vc = user_data;
struct voicecall_data *vd = ofono_voicecall_get_data(vc);
GAtResultIter iter;
const char *num;
int type;
GSList *l;
struct ofono_call *call;
l = g_slist_find_custom(vd->calls,
GINT_TO_POINTER(CALL_STATUS_INCOMING),
at_util_call_compare_by_status);
if (l == NULL) {
ofono_error("CDIP for unknown call");
return;
}
/* We have already saw a CDIP for this call, no need to parse again */
if ((vd->flags & FLAG_NEED_CDIP) == 0)
return;
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "+CDIP:"))
return;
if (!g_at_result_iter_next_string(&iter, &num))
return;
if (!g_at_result_iter_next_number(&iter, &type))
return;
DBG("%s %d", num, type);
call = l->data;
strncpy(call->called_number.number, num,
OFONO_MAX_PHONE_NUMBER_LENGTH);
call->called_number.number[OFONO_MAX_PHONE_NUMBER_LENGTH] = '\0';
call->called_number.type = type;
/* Only signal the call here if we already signaled it to the core */
if (call->type == 0 && (vd->flags & FLAG_NEED_CLIP) == 0)
ofono_voicecall_notify(vc, call);
vd->flags &= ~FLAG_NEED_CDIP;
}
static void cnap_notify(GAtResult *result, gpointer user_data)
{
struct ofono_voicecall *vc = user_data;
struct voicecall_data *vd = ofono_voicecall_get_data(vc);
GAtResultIter iter;
const char *name;
int validity;
GSList *l;
struct ofono_call *call;
l = g_slist_find_custom(vd->calls,
GINT_TO_POINTER(CALL_STATUS_INCOMING),
at_util_call_compare_by_status);
if (l == NULL) {
ofono_error("CNAP for unknown call");
return;
}
/* We have already saw a CLIP for this call, no need to parse again */
if ((vd->flags & FLAG_NEED_CNAP) == 0)
return;
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "+CNAP:"))
return;
if (!g_at_result_iter_next_string(&iter, &name))
return;
if (strlen(name) > 0)
validity = CNAP_VALIDITY_VALID;
else
validity = CNAP_VALIDITY_NOT_AVAILABLE;
/* If we have CNI validity field, override our guessed value */
g_at_result_iter_next_number(&iter, &validity);
DBG("%s %d", name, validity);
call = l->data;
strncpy(call->name, name,
OFONO_MAX_CALLER_NAME_LENGTH);
call->name[OFONO_MAX_CALLER_NAME_LENGTH] = '\0';
call->cnap_validity = validity;
/* Only signal the call here if we already signaled it to the core */
if (call->type == 0 && (vd->flags & FLAG_NEED_CLIP) == 0)
ofono_voicecall_notify(vc, call);
vd->flags &= ~FLAG_NEED_CNAP;
}
static void ccwa_notify(GAtResult *result, gpointer user_data)
{
struct ofono_voicecall *vc = user_data;
struct voicecall_data *vd = ofono_voicecall_get_data(vc);
GAtResultIter iter;
const char *num;
int num_type, validity, cls;
struct ofono_call *call;
/* Some modems resend CCWA, ignore it the second time around */
if (g_slist_find_custom(vd->calls,
GINT_TO_POINTER(CALL_STATUS_WAITING),
at_util_call_compare_by_status))
return;
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "+CCWA:"))
return;
if (!g_at_result_iter_next_string(&iter, &num))
return;
if (!g_at_result_iter_next_number(&iter, &num_type))
return;
if (!g_at_result_iter_next_number(&iter, &cls))
return;
/* Skip alpha field */
g_at_result_iter_skip_next(&iter);
if (strlen(num) > 0)
validity = 0;
else
validity = 2;
/* If we have CLI validity field, override our guessed value */
g_at_result_iter_next_number(&iter, &validity);
DBG("%s %d %d %d", num, num_type, cls, validity);
call = create_call(vc, class_to_call_type(cls), 1, CALL_STATUS_WAITING,
num, num_type, validity);
if (call == NULL) {
ofono_error("Unable to malloc. Call management is fubar");
return;
}
if (call->type == 0) /* Only notify voice calls */
ofono_voicecall_notify(vc, call);
if (vd->clcc_source == 0)
vd->clcc_source = g_timeout_add(POLL_CLCC_INTERVAL,
poll_clcc, vc);
}
static void no_carrier_notify(GAtResult *result, gpointer user_data)
{
struct ofono_voicecall *vc = user_data;
struct voicecall_data *vd = ofono_voicecall_get_data(vc);
g_at_chat_send(vd->chat, "AT+CLCC", clcc_prefix,
clcc_poll_cb, vc, NULL);
}
static void no_answer_notify(GAtResult *result, gpointer user_data)
{
struct ofono_voicecall *vc = user_data;
struct voicecall_data *vd = ofono_voicecall_get_data(vc);
g_at_chat_send(vd->chat, "AT+CLCC", clcc_prefix,
clcc_poll_cb, vc, NULL);
}
static void busy_notify(GAtResult *result, gpointer user_data)
{
struct ofono_voicecall *vc = user_data;
struct voicecall_data *vd = ofono_voicecall_get_data(vc);
/* Call was rejected, most likely due to network congestion
* or UDUB on the other side
* TODO: Handle UDUB or other conditions somehow
*/
g_at_chat_send(vd->chat, "AT+CLCC", clcc_prefix,
clcc_poll_cb, vc, NULL);
}
static void cssi_notify(GAtResult *result, gpointer user_data)
{
struct ofono_voicecall *vc = user_data;
GAtResultIter iter;
int code, index;
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "+CSSI:"))
return;
if (!g_at_result_iter_next_number(&iter, &code))
return;
if (!g_at_result_iter_next_number(&iter, &index))
index = 0;
ofono_voicecall_ssn_mo_notify(vc, 0, code, index);
}
static void cssu_notify(GAtResult *result, gpointer user_data)
{
struct ofono_voicecall *vc = user_data;
GAtResultIter iter;
int code;
int index;
const char *num;
struct ofono_phone_number ph;
ph.number[0] = '\0';
ph.type = 129;
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "+CSSU:"))
return;
if (!g_at_result_iter_next_number(&iter, &code))
return;
if (!g_at_result_iter_next_number_default(&iter, -1, &index))
goto out;
if (!g_at_result_iter_next_string(&iter, &num))
goto out;
strncpy(ph.number, num, OFONO_MAX_PHONE_NUMBER_LENGTH);
if (!g_at_result_iter_next_number(&iter, &ph.type))
return;
out:
ofono_voicecall_ssn_mt_notify(vc, 0, code, index, &ph);
}
static void vtd_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct ofono_voicecall *vc = user_data;
struct voicecall_data *vd = ofono_voicecall_get_data(vc);
GAtResultIter iter;
int duration;
if (!ok)
return;
g_at_result_iter_init(&iter, result);
g_at_result_iter_next(&iter, "+VTD:");
if (!g_at_result_iter_next_number(&iter, &duration))
return;
if (duration)
vd->tone_duration = duration * 100;
}
static void at_voicecall_initialized(gboolean ok, GAtResult *result,
gpointer user_data)
{
struct ofono_voicecall *vc = user_data;
struct voicecall_data *vd = ofono_voicecall_get_data(vc);
DBG("voicecall_init: registering to notifications");
g_at_chat_register(vd->chat, "RING", ring_notify, FALSE, vc, NULL);
g_at_chat_register(vd->chat, "+CRING:", cring_notify, FALSE, vc, NULL);
g_at_chat_register(vd->chat, "+CLIP:", clip_notify, FALSE, vc, NULL);
g_at_chat_register(vd->chat, "+CDIP:", cdip_notify, FALSE, vc, NULL);
g_at_chat_register(vd->chat, "+CNAP:", cnap_notify, FALSE, vc, NULL);
g_at_chat_register(vd->chat, "+CCWA:", ccwa_notify, FALSE, vc, NULL);
/* Modems with 'better' call progress indicators should
* probably not even bother registering to these
*/
g_at_chat_register(vd->chat, "NO CARRIER",
no_carrier_notify, FALSE, vc, NULL);
g_at_chat_register(vd->chat, "NO ANSWER",
no_answer_notify, FALSE, vc, NULL);
g_at_chat_register(vd->chat, "BUSY", busy_notify, FALSE, vc, NULL);
g_at_chat_register(vd->chat, "+CSSI:", cssi_notify, FALSE, vc, NULL);
g_at_chat_register(vd->chat, "+CSSU:", cssu_notify, FALSE, vc, NULL);
ofono_voicecall_register(vc);
/* Populate the call list */
g_at_chat_send(vd->chat, "AT+CLCC", clcc_prefix, clcc_cb, vc, NULL);
}
static int at_voicecall_probe(struct ofono_voicecall *vc, unsigned int vendor,
void *data)
{
GAtChat *chat = data;
struct voicecall_data *vd;
vd = g_try_new0(struct voicecall_data, 1);
if (vd == NULL)
return -ENOMEM;
vd->chat = g_at_chat_clone(chat);
vd->vendor = vendor;
vd->tone_duration = TONE_DURATION;
ofono_voicecall_set_data(vc, vd);
g_at_chat_send(vd->chat, "AT+CRC=1", NULL, NULL, NULL, NULL);
g_at_chat_send(vd->chat, "AT+CLIP=1", NULL, NULL, NULL, NULL);
if (vd->vendor != OFONO_VENDOR_TELIT)
{
g_at_chat_send(vd->chat, "AT+CDIP=1", NULL, NULL, NULL, NULL);
g_at_chat_send(vd->chat, "AT+CNAP=1", NULL, NULL, NULL, NULL);
}
switch (vd->vendor) {
case OFONO_VENDOR_QUALCOMM_MSM:
g_at_chat_send(vd->chat, "AT+COLP=0", NULL, NULL, NULL, NULL);
break;
default:
g_at_chat_send(vd->chat, "AT+COLP=1", NULL, NULL, NULL, NULL);
break;
}
g_at_chat_send(vd->chat, "AT+CSSN=1,1", NULL, NULL, NULL, NULL);
g_at_chat_send(vd->chat, "AT+VTD?", NULL,
vtd_query_cb, vc, NULL);
g_at_chat_send(vd->chat, "AT+CCWA=1", NULL,
at_voicecall_initialized, vc, NULL);
return 0;
}
static void at_voicecall_remove(struct ofono_voicecall *vc)
{
struct voicecall_data *vd = ofono_voicecall_get_data(vc);
if (vd->clcc_source)
g_source_remove(vd->clcc_source);
if (vd->vts_source)
g_source_remove(vd->vts_source);
g_slist_foreach(vd->calls, (GFunc) g_free, NULL);
g_slist_free(vd->calls);
ofono_voicecall_set_data(vc, NULL);
g_at_chat_unref(vd->chat);
g_free(vd);
}
static struct ofono_voicecall_driver driver = {
.name = "atmodem",
.probe = at_voicecall_probe,
.remove = at_voicecall_remove,
.dial = at_dial,
.answer = at_answer,
.hangup_all = at_hangup,
.hold_all_active = at_hold_all_active,
.release_all_held = at_release_all_held,
.set_udub = at_set_udub,
.release_all_active = at_release_all_active,
.release_specific = at_release_specific,
.private_chat = at_private_chat,
.create_multiparty = at_create_multiparty,
.transfer = at_transfer,
.deflect = at_deflect,
.swap_without_accept = NULL,
.send_tones = at_send_dtmf
};
void at_voicecall_init(void)
{
ofono_voicecall_driver_register(&driver);
}
void at_voicecall_exit(void)
{
ofono_voicecall_driver_unregister(&driver);
}
[-- Attachment #4: he910.c --]
[-- Type: text/plain, Size: 10523 bytes --]
/*
*
* oFono - Open Source Telephony
*
* Copyright (C) 2008-2014 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 <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <string.h>
#include <sys/socket.h>
#include <glib.h>
#include <gatchat.h>
#include <gattty.h>
#define OFONO_API_SUBJECT_TO_CHANGE
#include <ofono/plugin.h>
#include <ofono/log.h>
#include <ofono/modem.h>
#include <ofono/call-barring.h>
#include <ofono/call-forwarding.h>
#include <ofono/call-meter.h>
#include <ofono/call-settings.h>
#include <ofono/devinfo.h>
#include <ofono/message-waiting.h>
#include <ofono/location-reporting.h>
#include <ofono/netreg.h>
#include <ofono/phonebook.h>
#include <ofono/sim.h>
#include <ofono/gprs.h>
#include <ofono/gprs-context.h>
#include <ofono/sms.h>
#include <ofono/ussd.h>
#include <ofono/voicecall.h>
#include <drivers/atmodem/atutil.h>
#include <drivers/atmodem/vendor.h>
#define TELIT_CFUN_DELAY 3*1000*1000 /* uS */
static const char *none_prefix[] = { NULL };
static const char *qss_prefix[] = { "#QSS:", NULL };
struct he910_data {
GAtChat *chat; /* AT chat */
GAtChat *modem; /* Data port */
struct ofono_sim *sim;
ofono_bool_t have_sim;
ofono_bool_t sms_phonebook_added;
};
static void he910_debug(const char *str, void *user_data)
{
const char *prefix = user_data;
ofono_info("%s%s", prefix, str);
}
static GAtChat *open_device(struct ofono_modem *modem,
const char *key, char *debug)
{
const char *device;
GAtSyntax *syntax;
GIOChannel *channel;
GAtChat *chat;
GHashTable *options;
device = ofono_modem_get_string(modem, key);
if (device == NULL)
return NULL;
DBG("%s %s", key, device);
options = g_hash_table_new(g_str_hash, g_str_equal);
if (options == NULL)
return NULL;
g_hash_table_insert(options, "Baud", "115200");
channel = g_at_tty_open(device, options);
g_hash_table_destroy(options);
if (channel == NULL)
return NULL;
syntax = g_at_syntax_new_gsm_permissive();
chat = g_at_chat_new(channel, syntax);
g_at_syntax_unref(syntax);
g_io_channel_unref(channel);
if (chat == NULL)
return NULL;
if (getenv("OFONO_AT_DEBUG"))
g_at_chat_set_debug(chat, he910_debug, debug);
return chat;
}
static void switch_sim_state_status(struct ofono_modem *modem, int status)
{
struct he910_data *data = ofono_modem_get_data(modem);
DBG("%p, SIM status: %d", modem, status);
switch (status) {
case 0: /* SIM not inserted */
if (data->have_sim == TRUE) {
ofono_sim_inserted_notify(data->sim, FALSE);
data->have_sim = FALSE;
data->sms_phonebook_added = FALSE;
}
break;
case 1: /* SIM inserted */
case 2: /* SIM inserted and PIN unlocked */
if (data->have_sim == FALSE) {
ofono_sim_inserted_notify(data->sim, TRUE);
data->have_sim = TRUE;
}
break;
case 3: /* SIM inserted, SMS and phonebook ready */
if (data->sms_phonebook_added == FALSE) {
ofono_phonebook_create(modem, 0, "atmodem", data->chat);
ofono_sms_create(modem, OFONO_VENDOR_TELIT, "atmodem", data->chat);
data->sms_phonebook_added = TRUE;
}
break;
default:
ofono_warn("Unknown SIM state %d received", status);
break;
}
}
static void he910_qss_notify(GAtResult *result, gpointer user_data)
{
struct ofono_modem *modem = user_data;
int status;
GAtResultIter iter;
DBG("%p", modem);
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "#QSS:"))
return;
g_at_result_iter_next_number(&iter, &status);
switch_sim_state_status(modem, status);
}
static void qss_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct ofono_modem *modem = user_data;
int status, mode;
GAtResultIter iter;
DBG("%p", modem);
if (!ok)
return;
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "#QSS:"))
return;
if (!g_at_result_iter_next_number(&iter, &mode))
return;
if (!g_at_result_iter_next_number(&iter, &status))
return;
switch_sim_state_status(modem, status);
}
static void cfun_enable_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct ofono_modem *modem = user_data;
struct he910_data *data = ofono_modem_get_data(modem);
DBG("%p", modem);
if (!ok) {
g_at_chat_unref(data->chat);
data->chat = NULL;
g_at_chat_unref(data->modem);
data->modem = NULL;
ofono_modem_set_powered(modem, FALSE);
return;
}
/*
* Switch data carrier detect signal off.
* When the DCD is disabled the modem does not hangup anymore
* after the data connection.
*/
g_at_chat_send(data->modem, "AT&C0", NULL, NULL, NULL, NULL);
g_at_chat_send(data->chat, "AT&C0", NULL, NULL, NULL, NULL);
data->have_sim = FALSE;
data->sms_phonebook_added = FALSE;
ofono_modem_set_powered(modem, TRUE);
/* Follow sim state */
g_at_chat_register(data->chat, "#QSS:", he910_qss_notify,
FALSE, modem, NULL);
g_at_chat_send(data->chat, "AT#QSS?", qss_prefix,
qss_query_cb, modem, NULL);
}
static void cfun_delay_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct ofono_modem *modem = user_data;
struct he910_data *data = ofono_modem_get_data(modem);
DBG("%p", modem);
if (!ok) {
g_at_chat_unref(data->chat);
data->chat = NULL;
g_at_chat_unref(data->modem);
data->modem = NULL;
ofono_modem_set_powered(modem, FALSE);
return;
}
DBG("Wait for AT+CFUN=4 command completion");
usleep(TELIT_CFUN_DELAY);
/* Set phone functionality */
g_at_chat_send(data->chat, "AT+CFUN=1", none_prefix,
cfun_enable_cb, modem, NULL);
}
static int he910_enable(struct ofono_modem *modem)
{
struct he910_data *data = ofono_modem_get_data(modem);
DBG("%p", modem);
data->modem = open_device(modem, "Modem", "Modem: ");
if (data->modem == NULL)
return -EINVAL;
data->chat = open_device(modem, "Aux", "Aux: ");
if (data->chat == NULL) {
g_at_chat_unref(data->modem);
data->modem = NULL;
return -EIO;
}
g_at_chat_set_slave(data->modem, data->chat);
/*
* Disable command echo and
* enable the Extended Error Result Codes
*/
g_at_chat_send(data->chat, "ATE0 +CMEE=1", none_prefix,
NULL, NULL, NULL);
/* Set QSS notifications before restarting the SIM */
g_at_chat_send(data->chat, "AT#QSS=2", none_prefix, NULL, modem, NULL);
/* Power off module to get alla notifications on startup */
g_at_chat_send(data->chat, "AT+CFUN=4", none_prefix, cfun_delay_cb, modem, NULL);
return -EINPROGRESS;
}
static void cfun_disable_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct ofono_modem *modem = user_data;
struct he910_data *data = ofono_modem_get_data(modem);
DBG("%p", modem);
g_at_chat_unref(data->chat);
data->chat = NULL;
if (ok)
ofono_modem_set_powered(modem, FALSE);
}
static int he910_disable(struct ofono_modem *modem)
{
struct he910_data *data = ofono_modem_get_data(modem);
DBG("%p", modem);
g_at_chat_cancel_all(data->modem);
g_at_chat_unregister_all(data->modem);
g_at_chat_unref(data->modem);
data->modem = NULL;
g_at_chat_cancel_all(data->chat);
g_at_chat_unregister_all(data->chat);
/* Power down modem */
g_at_chat_send(data->chat, "AT+CFUN=4", none_prefix,
cfun_disable_cb, modem, NULL);
return -EINPROGRESS;
}
static void he910_pre_sim(struct ofono_modem *modem)
{
struct he910_data *data = ofono_modem_get_data(modem);
DBG("%p", modem);
ofono_devinfo_create(modem, 0, "atmodem", data->chat);
data->sim = ofono_sim_create(modem, OFONO_VENDOR_TELIT, "atmodem",
data->chat);
ofono_location_reporting_create(modem, 0, "telitmodem", data->chat);
}
static void he910_post_online(struct ofono_modem *modem)
{
struct he910_data *data = ofono_modem_get_data(modem);
struct ofono_message_waiting *mw;
struct ofono_gprs *gprs;
struct ofono_gprs_context *gc;
DBG("%p", modem);
ofono_voicecall_create(modem, 0, "atmodem", data->chat);
ofono_netreg_create(modem, OFONO_VENDOR_TELIT, "atmodem", data->chat);
ofono_ussd_create(modem, OFONO_VENDOR_TELIT, "atmodem", data->chat);
ofono_call_forwarding_create(modem, 0, "atmodem", data->chat);
ofono_call_settings_create(modem, 0, "atmodem", data->chat);
ofono_call_meter_create(modem, 0, "atmodem", data->chat);
ofono_call_barring_create(modem, 0, "atmodem", data->chat);
mw = ofono_message_waiting_create(modem);
if (mw)
ofono_message_waiting_register(mw);
gprs = ofono_gprs_create(modem, OFONO_VENDOR_TELIT, "atmodem",
data->chat);
gc = ofono_gprs_context_create(modem, 0, "atmodem", data->modem);
if (gprs && gc)
ofono_gprs_add_context(gprs, gc);
g_at_chat_send(data->chat, "AT#AUTOATT=1", none_prefix, NULL, NULL, NULL);
}
static int he910_probe(struct ofono_modem *modem)
{
struct he910_data *data;
DBG("%p", modem);
data = g_try_new0(struct he910_data, 1);
if (data == NULL)
return -ENOMEM;
ofono_modem_set_data(modem, data);
return 0;
}
static void he910_remove(struct ofono_modem *modem)
{
struct he910_data *data = ofono_modem_get_data(modem);
DBG("%p", modem);
ofono_modem_set_data(modem, NULL);
/* Cleanup after hot-unplug */
g_at_chat_unref(data->chat);
g_at_chat_unref(data->modem);
g_free(data);
}
static struct ofono_modem_driver he910_driver = {
.name = "he910",
.probe = he910_probe,
.remove = he910_remove,
.enable = he910_enable,
.disable = he910_disable,
.pre_sim = he910_pre_sim,
.post_online = he910_post_online,
};
static int he910_init(void)
{
DBG("");
return ofono_modem_driver_register(&he910_driver);
}
static void he910_exit(void)
{
ofono_modem_driver_unregister(&he910_driver);
}
OFONO_PLUGIN_DEFINE(he910, "Telit HE910 driver", VERSION,
OFONO_PLUGIN_PRIORITY_DEFAULT, he910_init, he910_exit)
[-- Attachment #5: sim.c --]
[-- Type: text/plain, Size: 75905 bytes --]
/*
*
* oFono - Open Source Telephony
*
* Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#define _GNU_SOURCE
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <glib.h>
#include <gdbus.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include "ofono.h"
#include "common.h"
#include "util.h"
#include "smsutil.h"
#include "simutil.h"
#include "storage.h"
#include "simfs.h"
#include "stkutil.h"
#define SIM_FLAG_READING_SPN 0x1
struct ofono_sim {
int flags;
/* Contents of the SIM file system, in rough initialization order */
char *iccid;
char **language_prefs;
unsigned char *efli;
unsigned char efli_length;
gboolean language_prefs_update;
enum ofono_sim_password_type pin_type;
gboolean locked_pins[OFONO_SIM_PASSWORD_SIM_PUK]; /* Number of PINs */
int pin_retries[OFONO_SIM_PASSWORD_INVALID];
enum ofono_sim_phase phase;
unsigned char mnc_length;
enum ofono_sim_cphs_phase cphs_phase;
unsigned char cphs_service_table[2];
unsigned char *efust;
unsigned char efust_length;
unsigned char *efest;
unsigned char efest_length;
unsigned char *efsst;
unsigned char efsst_length;
gboolean fixed_dialing;
gboolean barred_dialing;
char *imsi;
char mcc[OFONO_MAX_MCC_LENGTH + 1];
char mnc[OFONO_MAX_MNC_LENGTH + 1];
GSList *own_numbers;
GSList *new_numbers;
unsigned char efmsisdn_length;
unsigned char efmsisdn_records;
GSList *service_numbers;
gboolean sdn_ready;
unsigned char *efimg;
unsigned short efimg_length;
enum ofono_sim_state state;
struct ofono_watchlist *state_watches;
char *spn;
char *spn_dc;
struct ofono_watchlist *spn_watches;
unsigned int ef_spn_watch;
unsigned int cphs_spn_watch;
unsigned int cphs_spn_short_watch;
struct sim_fs *simfs;
struct ofono_sim_context *context;
struct ofono_sim_context *early_context;
unsigned char *iidf_image;
unsigned int *iidf_watch_ids;
DBusMessage *pending;
const struct ofono_sim_driver *driver;
void *driver_data;
struct ofono_atom *atom;
unsigned int hfp_watch;
};
struct msisdn_set_request {
struct ofono_sim *sim;
int pending;
int failed;
DBusMessage *msg;
};
struct service_number {
char *id;
struct ofono_phone_number ph;
};
static const char *const passwd_name[] = {
[OFONO_SIM_PASSWORD_NONE] = "none",
[OFONO_SIM_PASSWORD_SIM_PIN] = "pin",
[OFONO_SIM_PASSWORD_SIM_PUK] = "puk",
[OFONO_SIM_PASSWORD_PHSIM_PIN] = "phone",
[OFONO_SIM_PASSWORD_PHFSIM_PIN] = "firstphone",
[OFONO_SIM_PASSWORD_PHFSIM_PUK] = "firstphonepuk",
[OFONO_SIM_PASSWORD_SIM_PIN2] = "pin2",
[OFONO_SIM_PASSWORD_SIM_PUK2] = "puk2",
[OFONO_SIM_PASSWORD_PHNET_PIN] = "network",
[OFONO_SIM_PASSWORD_PHNET_PUK] = "networkpuk",
[OFONO_SIM_PASSWORD_PHNETSUB_PIN] = "netsub",
[OFONO_SIM_PASSWORD_PHNETSUB_PUK] = "netsubpuk",
[OFONO_SIM_PASSWORD_PHSP_PIN] = "service",
[OFONO_SIM_PASSWORD_PHSP_PUK] = "servicepuk",
[OFONO_SIM_PASSWORD_PHCORP_PIN] = "corp",
[OFONO_SIM_PASSWORD_PHCORP_PUK] = "corppuk",
};
static void sim_own_numbers_update(struct ofono_sim *sim);
static GSList *g_drivers = NULL;
static const char *sim_passwd_name(enum ofono_sim_password_type type)
{
return passwd_name[type];
}
static enum ofono_sim_password_type sim_string_to_passwd(const char *name)
{
int len = sizeof(passwd_name) / sizeof(*passwd_name);
int i;
for (i = 0; i < len; i++)
if (!strcmp(passwd_name[i], name))
return i;
return OFONO_SIM_PASSWORD_INVALID;
}
static gboolean password_is_pin(enum ofono_sim_password_type type)
{
switch (type) {
case OFONO_SIM_PASSWORD_SIM_PIN:
case OFONO_SIM_PASSWORD_PHSIM_PIN:
case OFONO_SIM_PASSWORD_PHFSIM_PIN:
case OFONO_SIM_PASSWORD_SIM_PIN2:
case OFONO_SIM_PASSWORD_PHNET_PIN:
case OFONO_SIM_PASSWORD_PHNETSUB_PIN:
case OFONO_SIM_PASSWORD_PHSP_PIN:
case OFONO_SIM_PASSWORD_PHCORP_PIN:
return TRUE;
case OFONO_SIM_PASSWORD_SIM_PUK:
case OFONO_SIM_PASSWORD_PHFSIM_PUK:
case OFONO_SIM_PASSWORD_SIM_PUK2:
case OFONO_SIM_PASSWORD_PHNET_PUK:
case OFONO_SIM_PASSWORD_PHNETSUB_PUK:
case OFONO_SIM_PASSWORD_PHSP_PUK:
case OFONO_SIM_PASSWORD_PHCORP_PUK:
case OFONO_SIM_PASSWORD_INVALID:
case OFONO_SIM_PASSWORD_NONE:
return FALSE;
}
return FALSE;
}
static enum ofono_sim_password_type puk2pin(enum ofono_sim_password_type type)
{
switch (type) {
case OFONO_SIM_PASSWORD_SIM_PUK:
return OFONO_SIM_PASSWORD_SIM_PIN;
case OFONO_SIM_PASSWORD_PHFSIM_PUK:
return OFONO_SIM_PASSWORD_PHFSIM_PIN;
case OFONO_SIM_PASSWORD_SIM_PUK2:
return OFONO_SIM_PASSWORD_SIM_PIN2;
case OFONO_SIM_PASSWORD_PHNET_PUK:
return OFONO_SIM_PASSWORD_PHNET_PIN;
case OFONO_SIM_PASSWORD_PHNETSUB_PUK:
return OFONO_SIM_PASSWORD_PHNETSUB_PIN;
case OFONO_SIM_PASSWORD_PHSP_PUK:
return OFONO_SIM_PASSWORD_PHSP_PIN;
case OFONO_SIM_PASSWORD_PHCORP_PUK:
return OFONO_SIM_PASSWORD_PHCORP_PIN;
default:
return OFONO_SIM_PASSWORD_INVALID;
}
}
static char **get_own_numbers(GSList *own_numbers)
{
int nelem = 0;
GSList *l;
struct ofono_phone_number *num;
char **ret;
if (own_numbers)
nelem = g_slist_length(own_numbers);
ret = g_new0(char *, nelem + 1);
nelem = 0;
for (l = own_numbers; l; l = l->next) {
num = l->data;
ret[nelem++] = g_strdup(phone_number_to_string(num));
}
return ret;
}
static char **get_locked_pins(struct ofono_sim *sim)
{
int i;
int nelem = 0;
char **ret;
for (i = 1; i < OFONO_SIM_PASSWORD_SIM_PUK; i++) {
if (sim->locked_pins[i] == FALSE)
continue;
nelem += 1;
}
ret = g_new0(char *, nelem + 1);
nelem = 0;
for (i = 1; i < OFONO_SIM_PASSWORD_SIM_PUK; i++) {
if (sim->locked_pins[i] == FALSE)
continue;
ret[nelem] = g_strdup(sim_passwd_name(i));
nelem += 1;
}
return ret;
}
static void get_pin_retries(struct ofono_sim *sim, void ***out_dict,
unsigned char **out_retries)
{
int i, nelem;
void **dict;
unsigned char *retries;
for (i = 1, nelem = 0; i < OFONO_SIM_PASSWORD_INVALID; i++) {
if (sim->pin_retries[i] == -1)
continue;
nelem += 1;
}
dict = g_new0(void *, nelem * 2 + 1);
retries = g_new0(unsigned char, nelem);
for (i = 1, nelem = 0; i < OFONO_SIM_PASSWORD_INVALID; i++) {
if (sim->pin_retries[i] == -1)
continue;
retries[nelem] = sim->pin_retries[i];
dict[nelem * 2] = (void *) sim_passwd_name(i);
dict[nelem * 2 + 1] = &retries[nelem];
nelem += 1;
}
*out_dict = dict;
*out_retries = retries;
}
static char **get_service_numbers(GSList *service_numbers)
{
int nelem;
GSList *l;
struct service_number *num;
char **ret;
nelem = g_slist_length(service_numbers) * 2;
ret = g_new0(char *, nelem + 1);
nelem = 0;
for (l = service_numbers; l; l = l->next) {
num = l->data;
ret[nelem++] = g_strdup(num->id);
ret[nelem++] = g_strdup(phone_number_to_string(&num->ph));
}
return ret;
}
static void service_number_free(struct service_number *num)
{
g_free(num->id);
g_free(num);
}
static void call_state_watches(struct ofono_sim *sim)
{
GSList *l;
ofono_sim_state_event_cb_t notify;
for (l = sim->state_watches->items; l; l = l->next) {
struct ofono_watchlist_item *item = l->data;
notify = item->notify;
notify(sim->state, item->notify_data);
}
}
static DBusMessage *sim_get_properties(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct ofono_sim *sim = data;
DBusMessage *reply;
DBusMessageIter iter;
DBusMessageIter dict;
char **own_numbers;
char **service_numbers;
char **locked_pins;
const char *pin_name;
void **pin_retries_dict;
unsigned char *dbus_retries;
dbus_bool_t present = sim->state != OFONO_SIM_STATE_NOT_PRESENT;
dbus_bool_t fdn;
dbus_bool_t bdn;
reply = dbus_message_new_method_return(msg);
if (reply == NULL)
return NULL;
dbus_message_iter_init_append(reply, &iter);
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
OFONO_PROPERTIES_ARRAY_SIGNATURE,
&dict);
ofono_dbus_dict_append(&dict, "Present", DBUS_TYPE_BOOLEAN, &present);
if (!present)
goto done;
if (sim->iccid)
ofono_dbus_dict_append(&dict, "CardIdentifier",
DBUS_TYPE_STRING, &sim->iccid);
if (sim->imsi)
ofono_dbus_dict_append(&dict, "SubscriberIdentity",
DBUS_TYPE_STRING, &sim->imsi);
fdn = sim->fixed_dialing;
ofono_dbus_dict_append(&dict, "FixedDialing", DBUS_TYPE_BOOLEAN, &fdn);
bdn = sim->barred_dialing;
ofono_dbus_dict_append(&dict, "BarredDialing", DBUS_TYPE_BOOLEAN, &bdn);
if (sim->mcc[0] != '\0' && sim->mnc[0] != '\0') {
const char *str;
str = sim->mcc;
ofono_dbus_dict_append(&dict, "MobileCountryCode",
DBUS_TYPE_STRING, &str);
str = sim->mnc;
ofono_dbus_dict_append(&dict, "MobileNetworkCode",
DBUS_TYPE_STRING, &str);
}
own_numbers = get_own_numbers(sim->own_numbers);
ofono_dbus_dict_append_array(&dict, "SubscriberNumbers",
DBUS_TYPE_STRING, &own_numbers);
g_strfreev(own_numbers);
locked_pins = get_locked_pins(sim);
ofono_dbus_dict_append_array(&dict, "LockedPins",
DBUS_TYPE_STRING, &locked_pins);
g_strfreev(locked_pins);
if (sim->service_numbers && sim->sdn_ready) {
service_numbers = get_service_numbers(sim->service_numbers);
ofono_dbus_dict_append_dict(&dict, "ServiceNumbers",
DBUS_TYPE_STRING,
&service_numbers);
g_strfreev(service_numbers);
}
if (sim->language_prefs)
ofono_dbus_dict_append_array(&dict, "PreferredLanguages",
DBUS_TYPE_STRING,
&sim->language_prefs);
pin_name = sim_passwd_name(sim->pin_type);
ofono_dbus_dict_append(&dict, "PinRequired",
DBUS_TYPE_STRING,
(void *) &pin_name);
get_pin_retries(sim, &pin_retries_dict, &dbus_retries);
ofono_dbus_dict_append_dict(&dict, "Retries", DBUS_TYPE_BYTE,
&pin_retries_dict);
g_free(pin_retries_dict);
g_free(dbus_retries);
done:
dbus_message_iter_close_container(&iter, &dict);
return reply;
}
static void sim_pin_retries_query_cb(const struct ofono_error *error,
int retries[OFONO_SIM_PASSWORD_INVALID],
void *data)
{
struct ofono_sim *sim = data;
DBusConnection *conn = ofono_dbus_get_connection();
const char *path = __ofono_atom_get_path(sim->atom);
void **pin_retries_dict;
unsigned char *dbus_retries;
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
ofono_error("Querying remaining pin retries failed");
return;
}
if (!memcmp(retries, sim->pin_retries, sizeof(sim->pin_retries)))
return;
memcpy(sim->pin_retries, retries, sizeof(sim->pin_retries));
get_pin_retries(sim, &pin_retries_dict, &dbus_retries);
ofono_dbus_signal_dict_property_changed(conn, path,
OFONO_SIM_MANAGER_INTERFACE, "Retries",
DBUS_TYPE_BYTE, &pin_retries_dict);
g_free(pin_retries_dict);
g_free(dbus_retries);
}
static void sim_pin_retries_check(struct ofono_sim *sim)
{
if (sim->driver->query_pin_retries == NULL)
return;
sim->driver->query_pin_retries(sim, sim_pin_retries_query_cb, sim);
}
static void msisdn_set_done(struct msisdn_set_request *req)
{
DBusMessage *reply;
if (req->failed)
reply = __ofono_error_failed(req->msg);
else
reply = dbus_message_new_method_return(req->msg);
__ofono_dbus_pending_reply(&req->msg, reply);
/* Re-read the numbers and emit signal if needed */
sim_own_numbers_update(req->sim);
g_free(req);
}
static void msisdn_set_cb(int ok, void *data)
{
struct msisdn_set_request *req = data;
if (!ok)
req->failed++;
req->pending--;
if (!req->pending)
msisdn_set_done(req);
}
static gboolean set_own_numbers(struct ofono_sim *sim,
GSList *new_numbers, DBusMessage *msg)
{
struct msisdn_set_request *req;
int record;
unsigned char efmsisdn[255];
struct ofono_phone_number *number;
if (new_numbers && g_slist_length(new_numbers) > sim->efmsisdn_records)
return FALSE;
req = g_new0(struct msisdn_set_request, 1);
req->sim = sim;
req->msg = dbus_message_ref(msg);
for (record = 1; record <= sim->efmsisdn_records; record++) {
if (new_numbers) {
number = new_numbers->data;
sim_adn_build(efmsisdn, sim->efmsisdn_length,
number, NULL);
new_numbers = new_numbers->next;
} else {
memset(efmsisdn, 0xff, sim->efmsisdn_length);
/* Set number length */
efmsisdn[sim->efmsisdn_length - 14] = 1;
}
if (ofono_sim_write(req->sim->context, SIM_EFMSISDN_FILEID,
msisdn_set_cb, OFONO_SIM_FILE_STRUCTURE_FIXED,
record, efmsisdn,
sim->efmsisdn_length, req) == 0)
req->pending++;
else
req->failed++;
}
if (!req->pending)
msisdn_set_done(req);
return TRUE;
}
static DBusMessage *sim_set_property(DBusConnection *conn, DBusMessage *msg,
void *data)
{
struct ofono_sim *sim = data;
DBusMessageIter iter;
DBusMessageIter var;
DBusMessageIter var_elem;
const char *name, *value;
if (!dbus_message_iter_init(msg, &iter))
return __ofono_error_invalid_args(msg);
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
return __ofono_error_invalid_args(msg);
dbus_message_iter_get_basic(&iter, &name);
if (!strcmp(name, "SubscriberNumbers")) {
gboolean set_ok = FALSE;
struct ofono_phone_number *own;
GSList *own_numbers = NULL;
if (sim->efmsisdn_length == 0)
return __ofono_error_busy(msg);
dbus_message_iter_next(&iter);
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
return __ofono_error_invalid_args(msg);
dbus_message_iter_recurse(&iter, &var);
if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_ARRAY ||
dbus_message_iter_get_element_type(&var) !=
DBUS_TYPE_STRING)
return __ofono_error_invalid_args(msg);
dbus_message_iter_recurse(&var, &var_elem);
/* Empty lists are supported */
while (dbus_message_iter_get_arg_type(&var_elem) !=
DBUS_TYPE_INVALID) {
if (dbus_message_iter_get_arg_type(&var_elem) !=
DBUS_TYPE_STRING)
goto error;
dbus_message_iter_get_basic(&var_elem, &value);
if (!valid_phone_number_format(value))
goto error;
own = g_new0(struct ofono_phone_number, 1);
string_to_phone_number(value, own);
own_numbers = g_slist_prepend(own_numbers, own);
dbus_message_iter_next(&var_elem);
}
own_numbers = g_slist_reverse(own_numbers);
set_ok = set_own_numbers(sim, own_numbers, msg);
error:
g_slist_foreach(own_numbers, (GFunc) g_free, 0);
g_slist_free(own_numbers);
if (set_ok)
return NULL;
}
return __ofono_error_invalid_args(msg);
}
static void sim_locked_cb(struct ofono_sim *sim, gboolean locked)
{
DBusConnection *conn = ofono_dbus_get_connection();
const char *path = __ofono_atom_get_path(sim->atom);
const char *typestr;
const char *pin;
char **locked_pins;
enum ofono_sim_password_type type;
DBusMessage *reply;
reply = dbus_message_new_method_return(sim->pending);
dbus_message_get_args(sim->pending, NULL, DBUS_TYPE_STRING, &typestr,
DBUS_TYPE_STRING, &pin,
DBUS_TYPE_INVALID);
type = sim_string_to_passwd(typestr);
/* This is used by lock/unlock pin, no puks allowed */
sim->locked_pins[type] = locked;
__ofono_dbus_pending_reply(&sim->pending, reply);
locked_pins = get_locked_pins(sim);
ofono_dbus_signal_array_property_changed(conn, path,
OFONO_SIM_MANAGER_INTERFACE,
"LockedPins", DBUS_TYPE_STRING,
&locked_pins);
g_strfreev(locked_pins);
sim_pin_retries_check(sim);
}
static void sim_unlock_cb(const struct ofono_error *error, void *data)
{
struct ofono_sim *sim = data;
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
DBusMessage *reply = __ofono_error_failed(sim->pending);
__ofono_dbus_pending_reply(&sim->pending, reply);
__ofono_sim_recheck_pin(sim);
return;
}
sim_locked_cb(sim, FALSE);
}
static void sim_lock_cb(const struct ofono_error *error, void *data)
{
struct ofono_sim *sim = data;
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
DBusMessage *reply = __ofono_error_failed(sim->pending);
__ofono_dbus_pending_reply(&sim->pending, reply);
__ofono_sim_recheck_pin(sim);
return;
}
sim_locked_cb(sim, TRUE);
}
static DBusMessage *sim_lock_or_unlock(struct ofono_sim *sim, int lock,
DBusConnection *conn, DBusMessage *msg)
{
enum ofono_sim_password_type type;
const char *typestr;
const char *pin;
if (sim->driver->lock == NULL)
return __ofono_error_not_implemented(msg);
if (sim->pending)
return __ofono_error_busy(msg);
if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &typestr,
DBUS_TYPE_STRING, &pin,
DBUS_TYPE_INVALID) == FALSE)
return __ofono_error_invalid_args(msg);
type = sim_string_to_passwd(typestr);
/*
* SIM PIN2 cannot be locked / unlocked according to 27.007,
* however the PIN combination can be changed
*/
if (password_is_pin(type) == FALSE ||
type == OFONO_SIM_PASSWORD_SIM_PIN2)
return __ofono_error_invalid_format(msg);
if (!__ofono_is_valid_sim_pin(pin, type))
return __ofono_error_invalid_format(msg);
sim->pending = dbus_message_ref(msg);
sim->driver->lock(sim, type, lock, pin,
lock ? sim_lock_cb : sim_unlock_cb, sim);
return NULL;
}
static DBusMessage *sim_lock_pin(DBusConnection *conn, DBusMessage *msg,
void *data)
{
struct ofono_sim *sim = data;
return sim_lock_or_unlock(sim, 1, conn, msg);
}
static DBusMessage *sim_unlock_pin(DBusConnection *conn, DBusMessage *msg,
void *data)
{
struct ofono_sim *sim = data;
return sim_lock_or_unlock(sim, 0, conn, msg);
}
static void sim_change_pin_cb(const struct ofono_error *error, void *data)
{
struct ofono_sim *sim = data;
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
__ofono_dbus_pending_reply(&sim->pending,
__ofono_error_failed(sim->pending));
__ofono_sim_recheck_pin(sim);
return;
}
__ofono_dbus_pending_reply(&sim->pending,
dbus_message_new_method_return(sim->pending));
sim_pin_retries_check(sim);
}
static DBusMessage *sim_change_pin(DBusConnection *conn, DBusMessage *msg,
void *data)
{
struct ofono_sim *sim = data;
enum ofono_sim_password_type type;
const char *typestr;
const char *old;
const char *new;
if (sim->driver->change_passwd == NULL)
return __ofono_error_not_implemented(msg);
if (sim->pending)
return __ofono_error_busy(msg);
if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &typestr,
DBUS_TYPE_STRING, &old,
DBUS_TYPE_STRING, &new,
DBUS_TYPE_INVALID) == FALSE)
return __ofono_error_invalid_args(msg);
type = sim_string_to_passwd(typestr);
if (password_is_pin(type) == FALSE)
return __ofono_error_invalid_format(msg);
if (!__ofono_is_valid_sim_pin(old, type))
return __ofono_error_invalid_format(msg);
if (!__ofono_is_valid_sim_pin(new, type))
return __ofono_error_invalid_format(msg);
if (!strcmp(new, old))
return dbus_message_new_method_return(msg);
sim->pending = dbus_message_ref(msg);
sim->driver->change_passwd(sim, type, old, new,
sim_change_pin_cb, sim);
return NULL;
}
static void sim_iccid_read_cb(int ok, int length, int record,
const unsigned char *data,
int record_length, void *userdata)
{
struct ofono_sim *sim = userdata;
const char *path = __ofono_atom_get_path(sim->atom);
DBusConnection *conn = ofono_dbus_get_connection();
char iccid[21]; /* ICCID max length is 20 + 1 for NULL */
if (!ok || length < 10)
return;
extract_bcd_number(data, length, iccid);
iccid[20] = '\0';
sim->iccid = g_strdup(iccid);
ofono_dbus_signal_property_changed(conn, path,
OFONO_SIM_MANAGER_INTERFACE,
"CardIdentifier",
DBUS_TYPE_STRING,
&sim->iccid);
}
static void sim_enter_pin_cb(const struct ofono_error *error, void *data)
{
struct ofono_sim *sim = data;
DBusMessage *reply;
if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
reply = __ofono_error_failed(sim->pending);
else
reply = dbus_message_new_method_return(sim->pending);
__ofono_dbus_pending_reply(&sim->pending, reply);
if (!sim->iccid)
ofono_sim_read(sim->early_context, SIM_EF_ICCID_FILEID,
OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
sim_iccid_read_cb, sim);
__ofono_sim_recheck_pin(sim);
}
static DBusMessage *sim_enter_pin(DBusConnection *conn, DBusMessage *msg,
void *data)
{
struct ofono_sim *sim = data;
const char *typestr;
enum ofono_sim_password_type type;
const char *pin;
if (sim->driver->send_passwd == NULL)
return __ofono_error_not_implemented(msg);
if (sim->pending)
return __ofono_error_busy(msg);
if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &typestr,
DBUS_TYPE_STRING, &pin,
DBUS_TYPE_INVALID) == FALSE)
return __ofono_error_invalid_args(msg);
type = sim_string_to_passwd(typestr);
if (type == OFONO_SIM_PASSWORD_NONE || type != sim->pin_type)
return __ofono_error_invalid_format(msg);
if (password_is_pin(type) == FALSE)
return __ofono_error_invalid_format(msg);
if (!__ofono_is_valid_sim_pin(pin, type))
return __ofono_error_invalid_format(msg);
sim->pending = dbus_message_ref(msg);
sim->driver->send_passwd(sim, pin, sim_enter_pin_cb, sim);
return NULL;
}
static void sim_get_image_cb(struct ofono_sim *sim,
unsigned char id, char *xpm, gboolean cache)
{
DBusMessage *reply;
DBusMessageIter iter, array;
int xpm_len;
if (xpm == NULL) {
reply = __ofono_error_failed(sim->pending);
__ofono_dbus_pending_reply(&sim->pending, reply);
return;
}
xpm_len = strlen(xpm);
reply = dbus_message_new_method_return(sim->pending);
dbus_message_iter_init_append(reply, &iter);
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
DBUS_TYPE_BYTE_AS_STRING, &array);
dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
&xpm, xpm_len);
dbus_message_iter_close_container(&iter, &array);
__ofono_dbus_pending_reply(&sim->pending, reply);
if (cache)
sim_fs_cache_image(sim->simfs, (const char *) xpm, id);
g_free(xpm);
}
static void sim_iidf_read_clut_cb(int ok, int length, int record,
const unsigned char *data,
int record_length, void *userdata)
{
struct ofono_sim *sim = userdata;
unsigned char id;
unsigned char *efimg;
unsigned short iidf_len;
unsigned short clut_len;
char *xpm;
DBG("ok: %d", ok);
dbus_message_get_args(sim->pending, NULL, DBUS_TYPE_BYTE, &id,
DBUS_TYPE_INVALID);
id -= 1;
efimg = &sim->efimg[id * 9];
if (!ok) {
sim_get_image_cb(sim, id, NULL, FALSE);
goto done;
}
iidf_len = efimg[7] << 8 | efimg[8];
if (sim->iidf_image[3] == 0)
clut_len = 256 * 3;
else
clut_len = sim->iidf_image[3] * 3;
xpm = stk_image_to_xpm(sim->iidf_image, iidf_len, efimg[2],
data, clut_len);
sim_get_image_cb(sim, id, xpm, TRUE);
done:
g_free(sim->iidf_image);
sim->iidf_image = NULL;
}
static void sim_iidf_read_cb(int ok, int length, int record,
const unsigned char *data,
int record_length, void *userdata)
{
struct ofono_sim *sim = userdata;
unsigned char id;
unsigned char *efimg;
unsigned short iidf_id;
unsigned short offset;
unsigned short clut_len;
unsigned char path[6];
unsigned int path_len;
DBG("ok: %d", ok);
dbus_message_get_args(sim->pending, NULL, DBUS_TYPE_BYTE, &id,
DBUS_TYPE_INVALID);
id -= 1;
efimg = &sim->efimg[id * 9];
if (!ok) {
sim_get_image_cb(sim, id, NULL, FALSE);
return;
}
if (efimg[2] == STK_IMG_SCHEME_BASIC) {
char *xpm = stk_image_to_xpm(data, length, efimg[2], NULL, 0);
sim_get_image_cb(sim, id, xpm, TRUE);
return;
}
offset = data[4] << 8 | data[5];
if (data[3] == 0)
clut_len = 256 * 3;
else
clut_len = data[3] * 3;
iidf_id = efimg[3] << 8 | efimg[4];
sim->iidf_image = g_memdup(data, length);
/* The path it the same between 2G and 3G */
path_len = sim_ef_db_get_path_3g(SIM_EFIMG_FILEID, path);
/* read the clut data */
ofono_sim_read_bytes(sim->context, iidf_id, offset, clut_len,
path, path_len,
sim_iidf_read_clut_cb, sim);
}
static void sim_image_data_changed(int id, void *userdata)
{
/* TODO: notify D-bus clients */
}
static void sim_get_image(struct ofono_sim *sim, unsigned char id,
gpointer user_data)
{
unsigned char *efimg;
char *image;
unsigned short iidf_id;
unsigned short iidf_offset;
unsigned short iidf_len;
if (sim->efimg_length <= id * 9) {
sim_get_image_cb(sim, id, NULL, FALSE);
return;
}
image = sim_fs_get_cached_image(sim->simfs, id);
if (image != NULL)
sim_get_image_cb(sim, id, image, FALSE);
efimg = &sim->efimg[id * 9];
iidf_id = efimg[3] << 8 | efimg[4];
iidf_offset = efimg[5] << 8 | efimg[6];
iidf_len = efimg[7] << 8 | efimg[8];
/* read the image data */
if (image == NULL) {
unsigned char path[6];
unsigned int path_len;
/* The path it the same between 2G and 3G */
path_len = sim_ef_db_get_path_3g(SIM_EFIMG_FILEID, path);
ofono_sim_read_bytes(sim->context, iidf_id, iidf_offset,
iidf_len, path, path_len,
sim_iidf_read_cb, sim);
}
if (sim->iidf_watch_ids[id] > 0)
return;
sim->iidf_watch_ids[id] = ofono_sim_add_file_watch(sim->context,
iidf_id, sim_image_data_changed,
sim, NULL);
}
static DBusMessage *sim_get_icon(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct ofono_sim *sim = data;
unsigned char id;
if (dbus_message_get_args(msg, NULL, DBUS_TYPE_BYTE, &id,
DBUS_TYPE_INVALID) == FALSE)
return __ofono_error_invalid_args(msg);
/* zero means no icon */
if (id == 0)
return __ofono_error_invalid_args(msg);
if (sim->pending)
return __ofono_error_busy(msg);
if (sim->efimg == NULL)
return __ofono_error_not_implemented(msg);
sim->pending = dbus_message_ref(msg);
sim_get_image(sim, id - 1, sim);
return NULL;
}
static DBusMessage *sim_reset_pin(DBusConnection *conn, DBusMessage *msg,
void *data)
{
struct ofono_sim *sim = data;
const char *typestr;
enum ofono_sim_password_type type;
const char *puk;
const char *pin;
if (sim->driver->reset_passwd == NULL)
return __ofono_error_not_implemented(msg);
if (sim->pending)
return __ofono_error_busy(msg);
if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &typestr,
DBUS_TYPE_STRING, &puk,
DBUS_TYPE_STRING, &pin,
DBUS_TYPE_INVALID) == FALSE)
return __ofono_error_invalid_args(msg);
type = sim_string_to_passwd(typestr);
if (type == OFONO_SIM_PASSWORD_NONE || type != sim->pin_type)
return __ofono_error_invalid_format(msg);
if (!__ofono_is_valid_sim_pin(puk, type))
return __ofono_error_invalid_format(msg);
type = puk2pin(type);
if (!__ofono_is_valid_sim_pin(pin, type))
return __ofono_error_invalid_format(msg);
sim->pending = dbus_message_ref(msg);
sim->driver->reset_passwd(sim, puk, pin, sim_enter_pin_cb, sim);
return NULL;
}
static const GDBusMethodTable sim_methods[] = {
{ GDBUS_METHOD("GetProperties",
NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
sim_get_properties) },
{ GDBUS_ASYNC_METHOD("SetProperty",
GDBUS_ARGS({ "property", "s" }, { "value", "v" }),
NULL, sim_set_property) },
{ GDBUS_ASYNC_METHOD("ChangePin",
GDBUS_ARGS({ "type", "s" }, { "oldpin", "s" },
{ "newpin", "s" }), NULL,
sim_change_pin) },
{ GDBUS_ASYNC_METHOD("EnterPin",
GDBUS_ARGS({ "type", "s" }, { "pin", "s" }), NULL,
sim_enter_pin) },
{ GDBUS_ASYNC_METHOD("ResetPin",
GDBUS_ARGS({ "type", "s" }, { "puk", "s" },
{ "newpin", "s" }), NULL,
sim_reset_pin) },
{ GDBUS_ASYNC_METHOD("LockPin",
GDBUS_ARGS({ "type", "s" }, { "pin", "s" }), NULL,
sim_lock_pin) },
{ GDBUS_ASYNC_METHOD("UnlockPin",
GDBUS_ARGS({ "type", "s" }, { "pin", "s" }), NULL,
sim_unlock_pin) },
{ GDBUS_ASYNC_METHOD("GetIcon",
GDBUS_ARGS({ "id", "y" }),
GDBUS_ARGS({ "icon", "ay" }),
sim_get_icon) },
{ }
};
static const GDBusSignalTable sim_signals[] = {
{ GDBUS_SIGNAL("PropertyChanged",
GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
{ }
};
static gboolean numbers_list_equal(GSList *a, GSList *b)
{
struct ofono_phone_number *num_a, *num_b;
while (a || b) {
if (a == NULL || b == NULL)
return FALSE;
num_a = a->data;
num_b = b->data;
if (!g_str_equal(num_a->number, num_b->number) ||
num_a->type != num_b->type)
return FALSE;
a = a->next;
b = b->next;
}
return TRUE;
}
static void sim_msisdn_read_cb(int ok, int length, int record,
const unsigned char *data,
int record_length, void *userdata)
{
struct ofono_sim *sim = userdata;
int total;
struct ofono_phone_number ph;
if (!ok)
goto check;
if (record_length < 14 || length < record_length) {
ofono_error("EFmsidn shall at least contain 14 bytes");
return;
}
total = length / record_length;
sim->efmsisdn_length = record_length;
sim->efmsisdn_records = total;
if (sim_adn_parse(data, record_length, &ph, NULL) == TRUE) {
struct ofono_phone_number *own;
own = g_new(struct ofono_phone_number, 1);
memcpy(own, &ph, sizeof(struct ofono_phone_number));
sim->new_numbers = g_slist_prepend(sim->new_numbers, own);
}
if (record != total)
return;
check:
/* All records retrieved */
if (sim->new_numbers)
sim->new_numbers = g_slist_reverse(sim->new_numbers);
if (!numbers_list_equal(sim->new_numbers, sim->own_numbers)) {
const char *path = __ofono_atom_get_path(sim->atom);
char **own_numbers;
DBusConnection *conn = ofono_dbus_get_connection();
g_slist_foreach(sim->own_numbers, (GFunc) g_free, NULL);
g_slist_free(sim->own_numbers);
sim->own_numbers = sim->new_numbers;
own_numbers = get_own_numbers(sim->own_numbers);
ofono_dbus_signal_array_property_changed(conn, path,
OFONO_SIM_MANAGER_INTERFACE,
"SubscriberNumbers",
DBUS_TYPE_STRING, &own_numbers);
g_strfreev(own_numbers);
} else {
g_slist_foreach(sim->new_numbers, (GFunc) g_free, NULL);
g_slist_free(sim->new_numbers);
}
sim->new_numbers = NULL;
}
static gint service_number_compare(gconstpointer a, gconstpointer b)
{
const struct service_number *sdn = a;
const char *id = b;
return strcmp(sdn->id, id);
}
static void sim_sdn_read_cb(int ok, int length, int record,
const unsigned char *data,
int record_length, void *userdata)
{
struct ofono_sim *sim = userdata;
DBusConnection *conn = ofono_dbus_get_connection();
const char *path = __ofono_atom_get_path(sim->atom);
int total;
struct ofono_phone_number ph;
char *alpha;
struct service_number *sdn;
if (!ok)
goto check;
if (record_length < 14 || length < record_length)
return;
total = length / record_length;
if (sim_adn_parse(data, record_length, &ph, &alpha) == FALSE)
goto out;
/* Use phone number if Id is unavailable */
if (alpha && alpha[0] == '\0') {
g_free(alpha);
alpha = NULL;
}
if (alpha == NULL)
alpha = g_strdup(phone_number_to_string(&ph));
if (sim->service_numbers &&
g_slist_find_custom(sim->service_numbers,
alpha, service_number_compare)) {
ofono_error("Duplicate EFsdn entries for `%s'",
alpha);
g_free(alpha);
goto out;
}
sdn = g_new(struct service_number, 1);
sdn->id = alpha;
memcpy(&sdn->ph, &ph, sizeof(struct ofono_phone_number));
sim->service_numbers = g_slist_prepend(sim->service_numbers, sdn);
out:
if (record != total)
return;
check:
/* All records retrieved */
if (sim->service_numbers) {
sim->service_numbers = g_slist_reverse(sim->service_numbers);
sim->sdn_ready = TRUE;
}
if (sim->sdn_ready) {
char **service_numbers;
service_numbers = get_service_numbers(sim->service_numbers);
ofono_dbus_signal_dict_property_changed(conn, path,
OFONO_SIM_MANAGER_INTERFACE,
"ServiceNumbers",
DBUS_TYPE_STRING,
&service_numbers);
g_strfreev(service_numbers);
}
}
static void sim_service_numbers_changed(int id, void *userdata)
{
struct ofono_sim *sim = userdata;
if (sim->service_numbers) {
g_slist_foreach(sim->service_numbers,
(GFunc)service_number_free, NULL);
g_slist_free(sim->service_numbers);
sim->service_numbers = NULL;
}
ofono_sim_read(sim->context, SIM_EFSDN_FILEID,
OFONO_SIM_FILE_STRUCTURE_FIXED, sim_sdn_read_cb, sim);
}
static void sim_own_numbers_update(struct ofono_sim *sim)
{
ofono_sim_read(sim->context, SIM_EFMSISDN_FILEID,
OFONO_SIM_FILE_STRUCTURE_FIXED, sim_msisdn_read_cb,
sim);
}
static void sim_own_numbers_changed(int id, void *userdata)
{
struct ofono_sim *sim = userdata;
sim_own_numbers_update(sim);
}
static void sim_efimg_read_cb(int ok, int length, int record,
const unsigned char *data,
int record_length, void *userdata)
{
struct ofono_sim *sim = userdata;
unsigned char *efimg;
int num_records;
if (!ok)
return;
num_records = length / record_length;
/*
* EFimg descriptors are 9 bytes long.
* Byte 1 of the record is the number of descriptors per record.
*/
if ((record_length < 10) ||
((record_length % 9 != 2) && (record_length % 9 != 1)))
return;
if (sim->efimg == NULL) {
sim->efimg = g_try_malloc0(num_records * 9);
if (sim->efimg == NULL)
return;
sim->iidf_watch_ids = g_try_new0(unsigned int, num_records);
if (sim->iidf_watch_ids == NULL) {
g_free(sim->efimg);
sim->efimg = NULL;
return;
}
sim->efimg_length = num_records * 9;
}
/*
* TBD - if we have more than one descriptor per record,
* pick the nicest one. For now we use the first one.
*/
/* copy descriptor into slot for this record */
efimg = &sim->efimg[(record - 1) * 9];
memcpy(efimg, &data[1], 9);
}
static void sim_efimg_changed(int id, void *userdata)
{
struct ofono_sim *sim = userdata;
int i, watch;
if (sim->efimg != NULL) {
for (i = sim->efimg_length / 9 - 1; i >= 0; i--) {
watch = sim->iidf_watch_ids[i];
if (watch == 0)
continue;
ofono_sim_remove_file_watch(sim->context, watch);
}
g_free(sim->efimg);
sim->efimg = NULL;
sim->efimg_length = 0;
g_free(sim->iidf_watch_ids);
sim->iidf_watch_ids = NULL;
}
ofono_sim_read(sim->context, SIM_EFIMG_FILEID,
OFONO_SIM_FILE_STRUCTURE_FIXED, sim_efimg_read_cb, sim);
/* TODO: notify D-bus clients */
}
static void sim_ready(enum ofono_sim_state new_state, void *user)
{
struct ofono_sim *sim = user;
if (new_state != OFONO_SIM_STATE_READY)
return;
sim_own_numbers_update(sim);
ofono_sim_add_file_watch(sim->context, SIM_EFMSISDN_FILEID,
sim_own_numbers_changed, sim, NULL);
ofono_sim_read(sim->context, SIM_EFSDN_FILEID,
OFONO_SIM_FILE_STRUCTURE_FIXED, sim_sdn_read_cb, sim);
ofono_sim_add_file_watch(sim->context, SIM_EFSDN_FILEID,
sim_service_numbers_changed, sim, NULL);
ofono_sim_read(sim->context, SIM_EFIMG_FILEID,
OFONO_SIM_FILE_STRUCTURE_FIXED, sim_efimg_read_cb, sim);
ofono_sim_add_file_watch(sim->context, SIM_EFIMG_FILEID,
sim_efimg_changed, sim, NULL);
}
static void sim_set_ready(struct ofono_sim *sim)
{
if (sim == NULL)
return;
if (sim->state != OFONO_SIM_STATE_INSERTED &&
sim->state != OFONO_SIM_STATE_LOCKED_OUT)
return;
sim->state = OFONO_SIM_STATE_READY;
sim_fs_check_version(sim->simfs);
call_state_watches(sim);
}
static void sim_imsi_obtained(struct ofono_sim *sim, const char *imsi)
{
DBusConnection *conn = ofono_dbus_get_connection();
const char *path = __ofono_atom_get_path(sim->atom);
sim->imsi = g_strdup(imsi);
ofono_dbus_signal_property_changed(conn, path,
OFONO_SIM_MANAGER_INTERFACE,
"SubscriberIdentity",
DBUS_TYPE_STRING, &sim->imsi);
if (sim->mnc_length) {
const char *str;
strncpy(sim->mcc, sim->imsi, OFONO_MAX_MCC_LENGTH);
sim->mcc[OFONO_MAX_MCC_LENGTH] = '\0';
strncpy(sim->mnc, sim->imsi + OFONO_MAX_MCC_LENGTH,
sim->mnc_length);
sim->mnc[sim->mnc_length] = '\0';
str = sim->mcc;
ofono_dbus_signal_property_changed(conn, path,
OFONO_SIM_MANAGER_INTERFACE,
"MobileCountryCode",
DBUS_TYPE_STRING, &str);
str = sim->mnc;
ofono_dbus_signal_property_changed(conn, path,
OFONO_SIM_MANAGER_INTERFACE,
"MobileNetworkCode",
DBUS_TYPE_STRING, &str);
}
sim_set_ready(sim);
}
static void sim_imsi_cb(const struct ofono_error *error, const char *imsi,
void *data)
{
struct ofono_sim *sim = data;
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
ofono_error("Unable to read IMSI, emergency calls only");
return;
}
sim_imsi_obtained(sim, imsi);
}
static void sim_efimsi_cb(const struct ofono_error *error,
const unsigned char *data, int len, void *user)
{
struct ofono_sim *sim = user;
char imsi[17]; /* IMSI max length is 15 + 1 for NULL + 1 waste */
unsigned char imsi_len;
unsigned char parity;
if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
goto error;
if (len != 9)
goto error;
imsi_len = data[0];
if (imsi_len == 0 || imsi_len > 8)
goto error;
/* The low 3 bits of the first byte should be set to binary 001 */
if ((data[1] & 0x7) != 0x1)
goto error;
/* Save off the parity bit */
parity = (data[1] >> 3) & 1;
extract_bcd_number(data + 1, imsi_len, imsi);
imsi[16] = '\0';
if ((strlen(imsi + 1) % 2) != parity)
goto error;
sim_imsi_obtained(sim, imsi + 1);
return;
error:
ofono_error("Unable to read IMSI, emergency calls only");
}
static void sim_retrieve_imsi(struct ofono_sim *sim)
{
if (sim->driver->read_imsi) {
sim->driver->read_imsi(sim, sim_imsi_cb, sim);
return;
}
if (sim->driver->read_file_transparent == NULL) {
ofono_error("IMSI retrieval not implemented,"
" only emergency calls will be available");
return;
}
sim->driver->read_file_transparent(sim, SIM_EFIMSI_FILEID, 0, 9,
NULL, 0, sim_efimsi_cb, sim);
}
static void sim_fdn_enabled(struct ofono_sim *sim)
{
DBusConnection *conn = ofono_dbus_get_connection();
const char *path = __ofono_atom_get_path(sim->atom);
dbus_bool_t val;
sim->fixed_dialing = TRUE;
val = sim->fixed_dialing;
ofono_dbus_signal_property_changed(conn, path,
OFONO_SIM_MANAGER_INTERFACE,
"FixedDialing",
DBUS_TYPE_BOOLEAN, &val);
}
static void sim_bdn_enabled(struct ofono_sim *sim)
{
DBusConnection *conn = ofono_dbus_get_connection();
const char *path = __ofono_atom_get_path(sim->atom);
dbus_bool_t val;
sim->barred_dialing = TRUE;
val = sim->barred_dialing;
ofono_dbus_signal_property_changed(conn, path,
OFONO_SIM_MANAGER_INTERFACE,
"BarredDialing",
DBUS_TYPE_BOOLEAN, &val);
}
static void sim_efbdn_info_read_cb(int ok, unsigned char file_status,
int total_length, int record_length,
void *userdata)
{
struct ofono_sim *sim = userdata;
if (!ok)
goto out;
if (file_status & SIM_FILE_STATUS_VALID)
sim_bdn_enabled(sim);
out:
if (sim->fixed_dialing != TRUE &&
sim->barred_dialing != TRUE)
sim_retrieve_imsi(sim);
}
static gboolean check_bdn_status(struct ofono_sim *sim)
{
/*
* Check the status of Barred Dialing in the SIM-card
* (TS 11.11/TS 51.011, Section 11.5.1: BDN capability request).
* If BDN is allocated, activated in EFsst and EFbdn is validated,
* halt the SIM initialization.
*/
if (sim_sst_is_active(sim->efsst, sim->efsst_length,
SIM_SST_SERVICE_BDN)) {
sim_fs_read_info(sim->context, SIM_EFBDN_FILEID,
OFONO_SIM_FILE_STRUCTURE_FIXED,
sim_efbdn_info_read_cb, sim);
return TRUE;
}
return FALSE;
}
static void sim_efadn_info_read_cb(int ok, unsigned char file_status,
int total_length, int record_length,
void *userdata)
{
struct ofono_sim *sim = userdata;
if (!ok)
goto out;
if (!(file_status & SIM_FILE_STATUS_VALID))
sim_fdn_enabled(sim);
out:
if (check_bdn_status(sim) != TRUE) {
if (sim->fixed_dialing != TRUE &&
sim->barred_dialing != TRUE)
sim_retrieve_imsi(sim);
}
}
static void sim_efsst_read_cb(int ok, int length, int record,
const unsigned char *data,
int record_length, void *userdata)
{
struct ofono_sim *sim = userdata;
if (!ok)
goto out;
if (length < 2) {
ofono_error("EFsst shall contain at least two bytes");
goto out;
}
sim->efsst = g_memdup(data, length);
sim->efsst_length = length;
/*
* Check if Fixed Dialing is enabled in the SIM-card
* (TS 11.11/TS 51.011, Section 11.5.1: FDN capability request).
* If FDN is activated and ADN is invalidated,
* don't continue initialization routine.
*/
if (sim_sst_is_active(sim->efsst, sim->efsst_length,
SIM_SST_SERVICE_FDN)) {
sim_fs_read_info(sim->context, SIM_EFADN_FILEID,
OFONO_SIM_FILE_STRUCTURE_FIXED,
sim_efadn_info_read_cb, sim);
return;
}
if (check_bdn_status(sim) == TRUE)
return;
out:
sim_retrieve_imsi(sim);
}
static void sim_efest_read_cb(int ok, int length, int record,
const unsigned char *data,
int record_length, void *userdata)
{
struct ofono_sim *sim = userdata;
gboolean available;
if (!ok)
goto out;
if (length < 1) {
ofono_error("EFest shall contain at least one byte");
goto out;
}
sim->efest = g_memdup(data, length);
sim->efest_length = length;
/*
* Check if Fixed Dialing is enabled in the USIM-card
* (TS 31.102, Section 5.3.2: FDN capability request).
* If FDN is activated, don't continue initialization routine.
*/
available = sim_ust_is_available(sim->efust, sim->efust_length,
SIM_UST_SERVICE_FDN);
if (available && sim_est_is_active(sim->efest, sim->efest_length,
SIM_EST_SERVICE_FDN))
sim_fdn_enabled(sim);
/*
* Check the status of Barred Dialing in the USIM-card
* (TS 31.102, Section 5.3.2: BDN capability request).
* If BDN service is enabled, halt the USIM initialization.
*/
available = sim_ust_is_available(sim->efust, sim->efust_length,
SIM_UST_SERVICE_BDN);
if (available && sim_est_is_active(sim->efest, sim->efest_length,
SIM_EST_SERVICE_BDN))
sim_bdn_enabled(sim);
out:
if (sim->fixed_dialing != TRUE &&
sim->barred_dialing != TRUE)
sim_retrieve_imsi(sim);
}
static void sim_efust_read_cb(int ok, int length, int record,
const unsigned char *data,
int record_length, void *userdata)
{
struct ofono_sim *sim = userdata;
if (!ok)
goto out;
if (length < 1) {
ofono_error("EFust shall contain at least one byte");
goto out;
}
sim->efust = g_memdup(data, length);
sim->efust_length = length;
/*
* Check whether the SIM provides EFest file
* According to 3GPP TS 31.102 section 4.2.47, EFest file
* shall be present if FDN or BDN or EST is available
* Lets be paranoid and check for the special cases as well
* where EST is not available(FDN or BDN available), but EFest
* is present
*/
if (sim_ust_is_available(sim->efust, sim->efust_length,
SIM_UST_SERVICE_ENABLED_SERVICE_TABLE) ||
sim_ust_is_available(sim->efust, sim->efust_length,
SIM_UST_SERVICE_FDN) ||
sim_ust_is_available(sim->efust, sim->efust_length,
SIM_UST_SERVICE_BDN)) {
ofono_sim_read(sim->context, SIM_EFEST_FILEID,
OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
sim_efest_read_cb, sim);
return;
}
out:
sim_retrieve_imsi(sim);
}
static void sim_cphs_information_read_cb(int ok, int length, int record,
const unsigned char *data,
int record_length, void *userdata)
{
struct ofono_sim *sim = userdata;
sim->cphs_phase = OFONO_SIM_CPHS_PHASE_NONE;
if (!ok || length < 3)
return;
if (data[0] == 0x01)
sim->cphs_phase = OFONO_SIM_CPHS_PHASE_1G;
else if (data[0] >= 0x02)
sim->cphs_phase = OFONO_SIM_CPHS_PHASE_2G;
memcpy(sim->cphs_service_table, data + 1, 2);
}
static void sim_ad_read_cb(int ok, int length, int record,
const unsigned char *data,
int record_length, void *userdata)
{
struct ofono_sim *sim = userdata;
int new_mnc_length;
if (!ok)
return;
if (length < 4) {
ofono_error("EFad should contain at least four bytes");
return;
}
new_mnc_length = data[3] & 0xf;
/* sanity check for potential invalid values */
if (new_mnc_length < 2 || new_mnc_length > 3)
return;
sim->mnc_length = new_mnc_length;
}
static void sim_efphase_read_cb(int ok, int length, int record,
const unsigned char *data,
int record_length, void *userdata)
{
struct ofono_sim *sim = userdata;
if (!ok || length != 1) {
sim->phase = OFONO_SIM_PHASE_3G;
ofono_sim_read(sim->context, SIM_EFUST_FILEID,
OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
sim_efust_read_cb, sim);
return;
}
switch (data[0]) {
case 0:
sim->phase = OFONO_SIM_PHASE_1G;
break;
case 2:
sim->phase = OFONO_SIM_PHASE_2G;
break;
case 3:
sim->phase = OFONO_SIM_PHASE_2G_PLUS;
break;
default:
ofono_error("Unknown phase");
return;
}
ofono_sim_read(sim->context, SIM_EFSST_FILEID,
OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
sim_efsst_read_cb, sim);
}
static void sim_initialize_after_pin(struct ofono_sim *sim)
{
sim->context = ofono_sim_context_create(sim);
sim->spn_watches = __ofono_watchlist_new(g_free);
ofono_sim_read(sim->context, SIM_EFPHASE_FILEID,
OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
sim_efphase_read_cb, sim);
ofono_sim_read(sim->context, SIM_EFAD_FILEID,
OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
sim_ad_read_cb, sim);
/*
* Read CPHS-support bits, this is still part of the SIM
* initialisation but no order is specified for it.
*/
ofono_sim_read(sim->context, SIM_EF_CPHS_INFORMATION_FILEID,
OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
sim_cphs_information_read_cb, sim);
}
static void sim_efli_read_cb(int ok, int length, int record,
const unsigned char *data,
int record_length, void *userdata)
{
struct ofono_sim *sim = userdata;
if (!ok)
return;
sim->efli = g_memdup(data, length);
sim->efli_length = length;
}
/* Detect whether the file is in EFli format, as opposed to 51.011 EFlp */
static gboolean sim_efli_format(const unsigned char *ef, int length)
{
int i;
if (length & 1)
return FALSE;
for (i = 0; i < length; i += 2) {
if (ef[i] == 0xff && ef[i+1] == 0xff)
continue;
/*
* ISO 639 country codes are each two lower-case SMS 7-bit
* characters while CB DCS language codes are in ranges
* (0 - 15) or (32 - 47), so the ranges don't overlap
*/
if (g_ascii_isalpha(ef[i]) == 0)
return FALSE;
if (g_ascii_isalpha(ef[i+1]) == 0)
return FALSE;
}
return TRUE;
}
static GSList *parse_language_list(const unsigned char *ef, int length)
{
int i;
GSList *ret = NULL;
for (i = 0; i < length; i += 2) {
if (ef[i] > 0x7f || ef[i+1] > 0x7f)
continue;
/*
* ISO 639 codes contain only characters that are coded
* identically in SMS 7 bit charset, ASCII or UTF8 so
* no conversion.
*/
ret = g_slist_prepend(ret, g_ascii_strdown((char *)ef + i, 2));
}
if (ret)
ret = g_slist_reverse(ret);
return ret;
}
static GSList *parse_eflp(const unsigned char *eflp, int length)
{
int i;
char code[3];
GSList *ret = NULL;
for (i = 0; i < length; i++) {
if (iso639_2_from_language(eflp[i], code) == FALSE)
continue;
ret = g_slist_prepend(ret, g_strdup(code));
}
if (ret)
ret = g_slist_reverse(ret);
return ret;
}
static char **concat_lang_prefs(GSList *a, GSList *b)
{
GSList *l, *k;
char **ret;
int i = 0;
int total = g_slist_length(a) + g_slist_length(b);
if (total == 0)
return NULL;
ret = g_new0(char *, total + 1);
for (l = a; l; l = l->next)
ret[i++] = g_strdup(l->data);
for (l = b; l; l = l->next) {
gboolean duplicate = FALSE;
for (k = a; k; k = k->next)
if (!strcmp(k->data, l->data))
duplicate = TRUE;
if (duplicate)
continue;
ret[i++] = g_strdup(l->data);
}
return ret;
}
static void sim_efpl_read_cb(int ok, int length, int record,
const unsigned char *data,
int record_length, void *userdata)
{
struct ofono_sim *sim = userdata;
const char *path = __ofono_atom_get_path(sim->atom);
DBusConnection *conn = ofono_dbus_get_connection();
gboolean efli_format = TRUE;
GSList *efli = NULL;
GSList *efpl = NULL;
if (!ok || length < 2)
goto skip_efpl;
efpl = parse_language_list(data, length);
skip_efpl:
if (sim->efli && sim->efli_length > 0) {
efli_format = sim_efli_format(sim->efli, sim->efli_length);
if (efli_format)
efli = parse_language_list(sim->efli, sim->efli_length);
else
efli = parse_eflp(sim->efli, sim->efli_length);
}
/*
* If efli_format is TRUE, make a list of languages in both files in
* order of preference following TS 31.102.
* Quoting 31.102 Section 5.1.1.2:
* The preferred language selection shall always use the EFLI in
* preference to the EFPL at the MF unless:
* - if the EFLI has the value 'FFFF' in its highest priority position,
* then the preferred language selection shall be the language
* preference in the EFPL at the MF level
* Otherwise in order of preference according to TS 51.011
*/
if (efli_format) {
if (sim->efli_length >= 2 && sim->efli[0] == 0xff &&
sim->efli[1] == 0xff)
sim->language_prefs = concat_lang_prefs(NULL, efpl);
else
sim->language_prefs = concat_lang_prefs(efli, efpl);
} else {
sim->language_prefs = concat_lang_prefs(efpl, efli);
}
if (sim->efli) {
g_free(sim->efli);
sim->efli = NULL;
sim->efli_length = 0;
}
if (efli) {
g_slist_foreach(efli, (GFunc)g_free, NULL);
g_slist_free(efli);
}
if (efpl) {
g_slist_foreach(efpl, (GFunc)g_free, NULL);
g_slist_free(efpl);
}
if (sim->language_prefs != NULL)
ofono_dbus_signal_array_property_changed(conn, path,
OFONO_SIM_MANAGER_INTERFACE,
"PreferredLanguages",
DBUS_TYPE_STRING,
&sim->language_prefs);
/* Proceed with sim initialization if we're not merely updating */
if (!sim->language_prefs_update)
__ofono_sim_recheck_pin(sim);
sim->language_prefs_update = FALSE;
}
static void sim_iccid_changed(int id, void *userdata)
{
struct ofono_sim *sim = userdata;
if (sim->iccid) {
g_free(sim->iccid);
sim->iccid = NULL;
}
ofono_sim_read(sim->early_context, SIM_EF_ICCID_FILEID,
OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
sim_iccid_read_cb, sim);
}
static void sim_efli_efpl_changed(int id, void *userdata)
{
struct ofono_sim *sim = userdata;
if (sim->efli != NULL) /* This shouldn't happen */
return;
if (sim->language_prefs) {
g_strfreev(sim->language_prefs);
sim->language_prefs = NULL;
}
sim->language_prefs_update = TRUE;
ofono_sim_read(sim->early_context, SIM_EFLI_FILEID,
OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
sim_efli_read_cb, sim);
ofono_sim_read(sim->early_context, SIM_EFPL_FILEID,
OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
sim_efpl_read_cb, sim);
}
static void sim_initialize(struct ofono_sim *sim)
{
/*
* Perform SIM initialization according to 3GPP 31.102 Section 5.1.1.2
* The assumption here is that if sim manager is being initialized,
* then sim commands are implemented, and the sim manager is then
* responsible for checking the PIN, reading the IMSI and signaling
* SIM ready condition.
*
* The procedure according to 31.102, 51.011, 11.11 and CPHS 4.2 is
* roughly:
*
* Read EFecc
* Read EFli and EFpl
* SIM Pin check
* Request SIM phase (only in 51.011)
* Administrative information request (read EFad)
* Request CPHS Information (only in CPHS 4.2)
* Read EFsst (only in 11.11 & 51.011)
* Read EFust (only in 31.102)
* Read EFest (only in 31.102)
* Read IMSI
*
* At this point we signal the SIM ready condition and allow
* arbitrary files to be written or read, assuming their presence
* in the EFust
*/
if (sim->early_context == NULL)
sim->early_context = ofono_sim_context_create(sim);
/* Grab the EFiccid which is always available */
ofono_sim_read(sim->early_context, SIM_EF_ICCID_FILEID,
OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
sim_iccid_read_cb, sim);
ofono_sim_add_file_watch(sim->early_context, SIM_EF_ICCID_FILEID,
sim_iccid_changed, sim, NULL);
/* EFecc is read by the voicecall atom */
/*
* According to 31.102 the EFli is read first and EFpl is then
* only read if none of the EFli languages are supported by user
* interface. 51.011 mandates the exact opposite, making EFpl/EFelp
* preferred over EFlp (same EFid as EFli, different format).
* However we don't depend on the user interface and so
* need to read both files now.
*/
ofono_sim_read(sim->early_context, SIM_EFLI_FILEID,
OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
sim_efli_read_cb, sim);
ofono_sim_add_file_watch(sim->early_context, SIM_EFLI_FILEID,
sim_efli_efpl_changed, sim, NULL);
ofono_sim_read(sim->early_context, SIM_EFPL_FILEID,
OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
sim_efpl_read_cb, sim);
ofono_sim_add_file_watch(sim->early_context, SIM_EFPL_FILEID,
sim_efli_efpl_changed, sim, NULL);
}
struct ofono_sim_context *ofono_sim_context_create(struct ofono_sim *sim)
{
if (sim == NULL || sim->simfs == NULL)
return NULL;
return sim_fs_context_new(sim->simfs);
}
void ofono_sim_context_free(struct ofono_sim_context *context)
{
return sim_fs_context_free(context);
}
int ofono_sim_read_bytes(struct ofono_sim_context *context, int id,
unsigned short offset, unsigned short num_bytes,
const unsigned char *path, unsigned int len,
ofono_sim_file_read_cb_t cb, void *data)
{
if (num_bytes == 0)
return -1;
return sim_fs_read(context, id, OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
offset, num_bytes, path, len, cb, data);
}
int ofono_sim_read(struct ofono_sim_context *context, int id,
enum ofono_sim_file_structure expected_type,
ofono_sim_file_read_cb_t cb, void *data)
{
return sim_fs_read(context, id, expected_type, 0, 0, NULL, 0, cb, data);
}
int ofono_sim_write(struct ofono_sim_context *context, int id,
ofono_sim_file_write_cb_t cb,
enum ofono_sim_file_structure structure, int record,
const unsigned char *data, int length, void *userdata)
{
return sim_fs_write(context, id, cb, structure, record, data, length,
userdata);
}
unsigned int ofono_sim_add_file_watch(struct ofono_sim_context *context,
int id, ofono_sim_file_changed_cb_t cb,
void *userdata,
ofono_destroy_func destroy)
{
return sim_fs_file_watch_add(context, id, cb, userdata, destroy);
}
void ofono_sim_remove_file_watch(struct ofono_sim_context *context,
unsigned int id)
{
sim_fs_file_watch_remove(context, id);
}
const char *ofono_sim_get_imsi(struct ofono_sim *sim)
{
if (sim == NULL)
return NULL;
return sim->imsi;
}
const char *ofono_sim_get_mcc(struct ofono_sim *sim)
{
if (sim == NULL)
return NULL;
return sim->mcc;
}
const char *ofono_sim_get_mnc(struct ofono_sim *sim)
{
if (sim == NULL)
return NULL;
return sim->mnc;
}
const char *ofono_sim_get_spn(struct ofono_sim *sim)
{
if (sim == NULL)
return NULL;
return sim->spn;
}
enum ofono_sim_phase ofono_sim_get_phase(struct ofono_sim *sim)
{
if (sim == NULL)
return OFONO_SIM_PHASE_UNKNOWN;
return sim->phase;
}
enum ofono_sim_cphs_phase ofono_sim_get_cphs_phase(struct ofono_sim *sim)
{
if (sim == NULL)
return OFONO_SIM_CPHS_PHASE_NONE;
return sim->cphs_phase;
}
enum ofono_sim_password_type ofono_sim_get_password_type(struct ofono_sim *sim)
{
if (sim == NULL)
return OFONO_SIM_PASSWORD_NONE;
return sim->pin_type;
}
const unsigned char *ofono_sim_get_cphs_service_table(struct ofono_sim *sim)
{
if (sim == NULL)
return NULL;
return sim->cphs_service_table;
}
ofono_bool_t __ofono_sim_service_available(struct ofono_sim *sim,
int ust_service,
int sst_service)
{
if (sim->efust)
return sim_ust_is_available(sim->efust, sim->efust_length,
ust_service);
if (sim->efsst)
return sim_sst_is_active(sim->efsst, sim->efsst_length,
sst_service);
return FALSE;
}
ofono_bool_t __ofono_sim_cphs_service_available(struct ofono_sim *sim,
int cphs_service)
{
return sim_cphs_is_active(sim->cphs_service_table, cphs_service);
}
static void sim_inserted_update(struct ofono_sim *sim)
{
DBusConnection *conn = ofono_dbus_get_connection();
const char *path = __ofono_atom_get_path(sim->atom);
dbus_bool_t present = sim->state != OFONO_SIM_STATE_NOT_PRESENT;
ofono_dbus_signal_property_changed(conn, path,
OFONO_SIM_MANAGER_INTERFACE,
"Present",
DBUS_TYPE_BOOLEAN, &present);
}
static void sim_free_early_state(struct ofono_sim *sim)
{
if (sim->iccid) {
g_free(sim->iccid);
sim->iccid = NULL;
}
if (sim->efli) {
g_free(sim->efli);
sim->efli = NULL;
sim->efli_length = 0;
}
if (sim->language_prefs) {
g_strfreev(sim->language_prefs);
sim->language_prefs = NULL;
}
if (sim->early_context) {
ofono_sim_context_free(sim->early_context);
sim->early_context = NULL;
}
}
static void sim_spn_close(struct ofono_sim *sim)
{
if (sim->spn_watches) {
__ofono_watchlist_free(sim->spn_watches);
sim->spn_watches = NULL;
}
/*
* We have not initialized SPN logic at all yet, either because
* no netreg / gprs atom has been needed or we have not reached the
* post_sim state
*/
if (sim->ef_spn_watch == 0)
return;
ofono_sim_remove_file_watch(sim->context, sim->ef_spn_watch);
sim->ef_spn_watch = 0;
ofono_sim_remove_file_watch(sim->context, sim->cphs_spn_watch);
sim->cphs_spn_watch = 0;
if (sim->cphs_spn_short_watch) {
ofono_sim_remove_file_watch(sim->context,
sim->cphs_spn_short_watch);
sim->cphs_spn_short_watch = 0;
}
sim->flags &= ~SIM_FLAG_READING_SPN;
g_free(sim->spn);
sim->spn = NULL;
g_free(sim->spn_dc);
sim->spn_dc = NULL;
}
static void sim_free_main_state(struct ofono_sim *sim)
{
int i;
for (i = 0; i < OFONO_SIM_PASSWORD_INVALID; i++)
sim->pin_retries[i] = -1;
memset(sim->locked_pins, 0, sizeof(sim->locked_pins));
if (sim->imsi) {
g_free(sim->imsi);
sim->imsi = NULL;
}
sim->mcc[0] = '\0';
sim->mnc[0] = '\0';
if (sim->own_numbers) {
g_slist_foreach(sim->own_numbers, (GFunc)g_free, NULL);
g_slist_free(sim->own_numbers);
sim->own_numbers = NULL;
}
if (sim->service_numbers) {
g_slist_foreach(sim->service_numbers,
(GFunc)service_number_free, NULL);
g_slist_free(sim->service_numbers);
sim->service_numbers = NULL;
sim->sdn_ready = FALSE;
}
if (sim->efust) {
g_free(sim->efust);
sim->efust = NULL;
sim->efust_length = 0;
}
if (sim->efest) {
g_free(sim->efest);
sim->efest = NULL;
sim->efest_length = 0;
}
if (sim->efsst) {
g_free(sim->efsst);
sim->efsst = NULL;
sim->efsst_length = 0;
}
sim->phase = OFONO_SIM_PHASE_UNKNOWN;
sim->cphs_phase = OFONO_SIM_CPHS_PHASE_NONE;
sim->mnc_length = 0;
memset(sim->cphs_service_table, 0, 2);
if (sim->efimg) {
g_free(sim->efimg);
sim->efimg = NULL;
sim->efimg_length = 0;
g_free(sim->iidf_watch_ids);
sim->iidf_watch_ids = NULL;
}
g_free(sim->iidf_image);
sim->iidf_image = NULL;
sim->fixed_dialing = FALSE;
sim->barred_dialing = FALSE;
sim_spn_close(sim);
if (sim->context) {
ofono_sim_context_free(sim->context);
sim->context = NULL;
}
}
static void sim_free_state(struct ofono_sim *sim)
{
sim_free_early_state(sim);
sim_free_main_state(sim);
}
void ofono_sim_inserted_notify(struct ofono_sim *sim, ofono_bool_t inserted)
{
if (sim->state == OFONO_SIM_STATE_RESETTING && inserted) {
/*
* Start initialization procedure from after EFiccid,
* EFli and EFpl are retrieved.
*/
sim->state = OFONO_SIM_STATE_INSERTED;
__ofono_sim_recheck_pin(sim);
return;
}
if (inserted == TRUE && sim->state == OFONO_SIM_STATE_NOT_PRESENT)
sim->state = OFONO_SIM_STATE_INSERTED;
else if (inserted == FALSE && sim->state != OFONO_SIM_STATE_NOT_PRESENT)
sim->state = OFONO_SIM_STATE_NOT_PRESENT;
else
return;
if (!__ofono_atom_get_registered(sim->atom))
return;
sim_inserted_update(sim);
call_state_watches(sim);
if (inserted)
sim_initialize(sim);
else
sim_free_state(sim);
}
unsigned int ofono_sim_add_state_watch(struct ofono_sim *sim,
ofono_sim_state_event_cb_t notify,
void *data, ofono_destroy_func destroy)
{
struct ofono_watchlist_item *item;
DBG("%p", sim);
if (sim == NULL)
return 0;
if (notify == NULL)
return 0;
item = g_new0(struct ofono_watchlist_item, 1);
item->notify = notify;
item->destroy = destroy;
item->notify_data = data;
return __ofono_watchlist_add_item(sim->state_watches, item);
}
void ofono_sim_remove_state_watch(struct ofono_sim *sim, unsigned int id)
{
__ofono_watchlist_remove_item(sim->state_watches, id);
}
enum ofono_sim_state ofono_sim_get_state(struct ofono_sim *sim)
{
if (sim == NULL)
return OFONO_SIM_STATE_NOT_PRESENT;
return sim->state;
}
static void spn_watch_cb(gpointer data, gpointer user_data)
{
struct ofono_watchlist_item *item = data;
struct ofono_sim *sim = user_data;
if (item->notify)
((ofono_sim_spn_cb_t) item->notify)(sim->spn, sim->spn_dc,
item->notify_data);
}
static inline void spn_watches_notify(struct ofono_sim *sim)
{
if (sim->spn_watches->items)
g_slist_foreach(sim->spn_watches->items, spn_watch_cb, sim);
sim->flags &= ~SIM_FLAG_READING_SPN;
}
static void sim_spn_set(struct ofono_sim *sim, const void *data, int length,
const unsigned char *dc)
{
g_free(sim->spn);
sim->spn = NULL;
g_free(sim->spn_dc);
sim->spn_dc = NULL;
if (data == NULL)
goto notify;
/*
* TS 31.102 says:
*
* the string shall use:
*
* - either the SMS default 7-bit coded alphabet as defined in
* TS 23.038 [5] with bit 8 set to 0. The string shall be left
* justified. Unused bytes shall be set to 'FF'.
*
* - or one of the UCS2 code options defined in the annex of TS
* 31.101 [11].
*
* 31.101 has no such annex though. 51.101 refers to Annex B of
* itself which is not there either. 11.11 contains the same
* paragraph as 51.101 and has an Annex B which we implement.
*/
sim->spn = sim_string_to_utf8(data, length);
if (sim->spn == NULL) {
ofono_error("EFspn read successfully, but couldn't parse");
goto notify;
}
if (strlen(sim->spn) == 0) {
g_free(sim->spn);
sim->spn = NULL;
goto notify;
}
if (dc)
sim->spn_dc = g_memdup(dc, 1);
notify:
spn_watches_notify(sim);
}
static void sim_cphs_spn_short_read_cb(int ok, int length, int record,
const unsigned char *data,
int record_length, void *user_data)
{
struct ofono_sim *sim = user_data;
if (!ok) {
sim_spn_set(sim, NULL, 0, NULL);
return;
}
sim_spn_set(sim, data, length, NULL);
}
static void sim_cphs_spn_read_cb(int ok, int length, int record,
const unsigned char *data,
int record_length, void *user_data)
{
struct ofono_sim *sim = user_data;
if (!ok) {
if (__ofono_sim_cphs_service_available(sim,
SIM_CPHS_SERVICE_SHORT_SPN))
ofono_sim_read(sim->context,
SIM_EF_CPHS_SPN_SHORT_FILEID,
OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
sim_cphs_spn_short_read_cb, sim);
else
sim_spn_set(sim, NULL, 0, NULL);
return;
}
sim_spn_set(sim, data, length, NULL);
}
static void sim_spn_read_cb(int ok, int length, int record,
const unsigned char *data,
int record_length, void *user_data)
{
struct ofono_sim *sim = user_data;
if (!ok) {
ofono_sim_read(sim->context, SIM_EF_CPHS_SPN_FILEID,
OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
sim_cphs_spn_read_cb, sim);
return;
}
sim_spn_set(sim, data + 1, length - 1, data);
}
static void sim_spn_changed(int id, void *userdata)
{
struct ofono_sim *sim = userdata;
if (sim->flags & SIM_FLAG_READING_SPN)
return;
sim->flags |= SIM_FLAG_READING_SPN;
ofono_sim_read(sim->context, SIM_EFSPN_FILEID,
OFONO_SIM_FILE_STRUCTURE_TRANSPARENT,
sim_spn_read_cb, sim);
}
static void sim_spn_init(struct ofono_sim *sim)
{
sim->ef_spn_watch = ofono_sim_add_file_watch(sim->context,
SIM_EFSPN_FILEID, sim_spn_changed, sim,
NULL);
sim->cphs_spn_watch = ofono_sim_add_file_watch(sim->context,
SIM_EF_CPHS_SPN_FILEID,
sim_spn_changed, sim, NULL);
if (__ofono_sim_cphs_service_available(sim,
SIM_CPHS_SERVICE_SHORT_SPN))
sim->cphs_spn_short_watch = ofono_sim_add_file_watch(
sim->context, SIM_EF_CPHS_SPN_SHORT_FILEID,
sim_spn_changed, sim, NULL);
}
ofono_bool_t ofono_sim_add_spn_watch(struct ofono_sim *sim, unsigned int *id,
ofono_sim_spn_cb_t cb, void *data,
ofono_destroy_func destroy)
{
struct ofono_watchlist_item *item;
unsigned int watch_id;
DBG("%p", sim);
if (sim == NULL)
return 0;
item = g_new0(struct ofono_watchlist_item, 1);
item->notify = cb;
item->destroy = destroy;
item->notify_data = data;
watch_id = __ofono_watchlist_add_item(sim->spn_watches, item);
if (watch_id == 0)
return FALSE;
*id = watch_id;
if (sim->ef_spn_watch == 0) {
sim_spn_init(sim);
sim_spn_changed(0, sim);
return TRUE;
}
if (sim->flags & SIM_FLAG_READING_SPN)
return TRUE;
((ofono_sim_spn_cb_t) item->notify)(sim->spn, sim->spn_dc,
item->notify_data);
return TRUE;
}
ofono_bool_t ofono_sim_remove_spn_watch(struct ofono_sim *sim, unsigned int *id)
{
gboolean ret;
DBG("%p", sim);
if (sim == NULL)
return FALSE;
ret = __ofono_watchlist_remove_item(sim->spn_watches, *id);
if (ret == TRUE)
*id = 0;
return ret;
}
static void sim_pin_query_cb(const struct ofono_error *error,
enum ofono_sim_password_type pin_type,
void *data)
{
struct ofono_sim *sim = data;
DBusConnection *conn = ofono_dbus_get_connection();
const char *path = __ofono_atom_get_path(sim->atom);
const char *pin_name;
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
ofono_error("Querying PIN authentication state failed");
return;
}
if (sim->pin_type != pin_type) {
sim->pin_type = pin_type;
pin_name = sim_passwd_name(pin_type);
if (pin_type != OFONO_SIM_PASSWORD_NONE &&
password_is_pin(pin_type) == FALSE)
pin_type = puk2pin(pin_type);
if (pin_type != OFONO_SIM_PASSWORD_INVALID)
sim->locked_pins[pin_type] = TRUE;
ofono_dbus_signal_property_changed(conn, path,
OFONO_SIM_MANAGER_INTERFACE,
"PinRequired", DBUS_TYPE_STRING,
&pin_name);
}
switch (pin_type) {
case OFONO_SIM_PASSWORD_NONE:
case OFONO_SIM_PASSWORD_SIM_PIN2:
case OFONO_SIM_PASSWORD_SIM_PUK2:
break;
default:
if (sim->state == OFONO_SIM_STATE_READY) {
/* Force the sim state out of READY */
sim_free_main_state(sim);
sim->state = OFONO_SIM_STATE_LOCKED_OUT;
call_state_watches(sim);
}
break;
}
sim_pin_retries_check(sim);
switch (pin_type) {
case OFONO_SIM_PASSWORD_SIM_PIN2:
case OFONO_SIM_PASSWORD_SIM_PUK2:
case OFONO_SIM_PASSWORD_NONE:
if (sim->state == OFONO_SIM_STATE_READY)
break;
/* Fall through */
sim_initialize_after_pin(sim);
break;
default:
break;
}
}
void __ofono_sim_recheck_pin(struct ofono_sim *sim)
{
if (sim->driver->query_passwd_state == NULL) {
sim_initialize_after_pin(sim);
return;
}
sim->driver->query_passwd_state(sim, sim_pin_query_cb, sim);
}
int ofono_sim_driver_register(const struct ofono_sim_driver *d)
{
DBG("driver: %p, name: %s", d, d->name);
if (d->probe == NULL)
return -EINVAL;
g_drivers = g_slist_prepend(g_drivers, (void *) d);
return 0;
}
void ofono_sim_driver_unregister(const struct ofono_sim_driver *d)
{
DBG("driver: %p, name: %s", d, d->name);
g_drivers = g_slist_remove(g_drivers, (void *) d);
}
static void emulator_remove_handler(struct ofono_atom *atom, void *data)
{
struct ofono_emulator *em = __ofono_atom_get_data(atom);
ofono_emulator_remove_handler(em, data);
}
static void sim_unregister(struct ofono_atom *atom)
{
DBusConnection *conn = ofono_dbus_get_connection();
struct ofono_modem *modem = __ofono_atom_get_modem(atom);
const char *path = __ofono_atom_get_path(atom);
struct ofono_sim *sim = __ofono_atom_get_data(atom);
__ofono_modem_foreach_registered_atom(modem,
OFONO_ATOM_TYPE_EMULATOR_HFP,
emulator_remove_handler,
"+CNUM");
__ofono_modem_remove_atom_watch(modem, sim->hfp_watch);
__ofono_watchlist_free(sim->state_watches);
sim->state_watches = NULL;
g_dbus_unregister_interface(conn, path, OFONO_SIM_MANAGER_INTERFACE);
ofono_modem_remove_interface(modem, OFONO_SIM_MANAGER_INTERFACE);
}
static void sim_remove(struct ofono_atom *atom)
{
struct ofono_sim *sim = __ofono_atom_get_data(atom);
DBG("atom: %p", atom);
if (sim == NULL)
return;
if (sim->driver != NULL && sim->driver->remove != NULL)
sim->driver->remove(sim);
sim_free_state(sim);
sim_fs_free(sim->simfs);
sim->simfs = NULL;
g_free(sim);
}
struct ofono_sim *ofono_sim_create(struct ofono_modem *modem,
unsigned int vendor,
const char *driver,
void *data)
{
struct ofono_sim *sim;
GSList *l;
int i;
if (driver == NULL)
return NULL;
sim = g_try_new0(struct ofono_sim, 1);
if (sim == NULL)
return NULL;
sim->phase = OFONO_SIM_PHASE_UNKNOWN;
sim->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_SIM,
sim_remove, sim);
for (i = 0; i < OFONO_SIM_PASSWORD_INVALID; i++)
sim->pin_retries[i] = -1;
for (l = g_drivers; l; l = l->next) {
const struct ofono_sim_driver *drv = l->data;
if (g_strcmp0(drv->name, driver))
continue;
if (drv->probe(sim, vendor, data) < 0)
continue;
sim->driver = drv;
break;
}
return sim;
}
static void emulator_cnum_cb(struct ofono_emulator *em,
struct ofono_emulator_request *req, void *userdata)
{
struct ofono_sim *sim = userdata;
struct ofono_error result;
GSList *l;
const char *phone;
/*
* '+CNUM: ,"+",,,4' + phone number + phone type on 3 digits max
* + terminating null
*/
char buf[OFONO_MAX_PHONE_NUMBER_LENGTH + 18 + 1];
result.error = 0;
switch (ofono_emulator_request_get_type(req)) {
case OFONO_EMULATOR_REQUEST_TYPE_COMMAND_ONLY:
for (l = sim->own_numbers; l; l = l->next) {
struct ofono_phone_number *ph = l->data;
phone = phone_number_to_string(ph);
sprintf(buf, "+CNUM: ,\"%s\",%d,,4", phone, ph->type);
ofono_emulator_send_info(em, buf, l->next == NULL ?
TRUE : FALSE);
}
result.type = OFONO_ERROR_TYPE_NO_ERROR;
ofono_emulator_send_final(em, &result);
break;
default:
result.type = OFONO_ERROR_TYPE_FAILURE;
ofono_emulator_send_final(em, &result);
};
}
static void emulator_hfp_watch(struct ofono_atom *atom,
enum ofono_atom_watch_condition cond,
void *data)
{
struct ofono_emulator *em = __ofono_atom_get_data(atom);
if (cond == OFONO_ATOM_WATCH_CONDITION_REGISTERED)
ofono_emulator_add_handler(em, "+CNUM", emulator_cnum_cb, data,
NULL);
}
void ofono_sim_register(struct ofono_sim *sim)
{
DBusConnection *conn = ofono_dbus_get_connection();
struct ofono_modem *modem = __ofono_atom_get_modem(sim->atom);
const char *path = __ofono_atom_get_path(sim->atom);
if (!g_dbus_register_interface(conn, path,
OFONO_SIM_MANAGER_INTERFACE,
sim_methods, sim_signals, NULL,
sim, NULL)) {
ofono_error("Could not create %s interface",
OFONO_SIM_MANAGER_INTERFACE);
return;
}
ofono_modem_add_interface(modem, OFONO_SIM_MANAGER_INTERFACE);
sim->state_watches = __ofono_watchlist_new(g_free);
sim->simfs = sim_fs_new(sim, sim->driver);
__ofono_atom_register(sim->atom, sim_unregister);
ofono_sim_add_state_watch(sim, sim_ready, sim, NULL);
if (sim->state > OFONO_SIM_STATE_NOT_PRESENT)
sim_initialize(sim);
sim->hfp_watch = __ofono_modem_add_atom_watch(modem,
OFONO_ATOM_TYPE_EMULATOR_HFP,
emulator_hfp_watch, sim, NULL);
}
void ofono_sim_remove(struct ofono_sim *sim)
{
__ofono_atom_free(sim->atom);
}
void ofono_sim_set_data(struct ofono_sim *sim, void *data)
{
sim->driver_data = data;
}
void *ofono_sim_get_data(struct ofono_sim *sim)
{
return sim->driver_data;
}
static ofono_bool_t is_valid_pin(const char *pin, unsigned int min,
unsigned int max)
{
unsigned int i;
/* Pin must not be empty */
if (pin == NULL || pin[0] == '\0')
return FALSE;
i = strlen(pin);
if (i != strspn(pin, "0123456789"))
return FALSE;
if (min <= i && i <= max)
return TRUE;
return FALSE;
}
ofono_bool_t __ofono_is_valid_sim_pin(const char *pin,
enum ofono_sim_password_type type)
{
switch (type) {
case OFONO_SIM_PASSWORD_SIM_PIN:
case OFONO_SIM_PASSWORD_SIM_PIN2:
/* 11.11 Section 9.3 ("CHV"): 4..8 IA-5 digits */
return is_valid_pin(pin, 4, 8);
break;
case OFONO_SIM_PASSWORD_PHSIM_PIN:
case OFONO_SIM_PASSWORD_PHFSIM_PIN:
case OFONO_SIM_PASSWORD_PHNET_PIN:
case OFONO_SIM_PASSWORD_PHNETSUB_PIN:
case OFONO_SIM_PASSWORD_PHSP_PIN:
case OFONO_SIM_PASSWORD_PHCORP_PIN:
/* 22.022 Section 14 4..16 IA-5 digits */
return is_valid_pin(pin, 4, 16);
break;
case OFONO_SIM_PASSWORD_SIM_PUK:
case OFONO_SIM_PASSWORD_SIM_PUK2:
case OFONO_SIM_PASSWORD_PHFSIM_PUK:
case OFONO_SIM_PASSWORD_PHNET_PUK:
case OFONO_SIM_PASSWORD_PHNETSUB_PUK:
case OFONO_SIM_PASSWORD_PHSP_PUK:
case OFONO_SIM_PASSWORD_PHCORP_PUK:
/* 11.11 Section 9.3 ("UNBLOCK CHV"), 8 IA-5 digits */
return is_valid_pin(pin, 8, 8);
break;
case OFONO_SIM_PASSWORD_NONE:
return is_valid_pin(pin, 0, 8);
break;
case OFONO_SIM_PASSWORD_INVALID:
break;
}
return FALSE;
}
ofono_bool_t __ofono_is_valid_net_pin(const char *pin)
{
return is_valid_pin(pin, 4, 4);
}
static void sim_file_changed_flush(struct ofono_sim *sim, int id)
{
int i, imgid;
if (id == SIM_EFIMG_FILEID)
/* All cached images become invalid */
sim_fs_image_cache_flush(sim->simfs);
else if (sim->efimg) {
/*
* Data and CLUT for image instances stored in the changed
* file need to be re-read.
*/
for (i = sim->efimg_length / 9 - 1; i >= 0; i--) {
imgid = (sim->efimg[i * 9 + 3] << 8) |
sim->efimg[i * 9 + 4];
if (imgid == id)
sim_fs_image_cache_flush_file(sim->simfs, i);
}
}
sim_fs_cache_flush_file(sim->simfs, id);
}
void __ofono_sim_refresh(struct ofono_sim *sim, GSList *file_list,
ofono_bool_t full_file_change, ofono_bool_t naa_init)
{
GSList *l;
gboolean reinit_naa = naa_init || full_file_change;
/*
* Check if any files used in SIM initialisation procedure
* are affected, except EFiccid, EFpl, EFli.
*/
for (l = file_list; l; l = l->next) {
struct stk_file *file = l->data;
uint32_t mf, df, ef;
if (file->len != 6)
continue;
mf = (file->file[0] << 8) | (file->file[1] << 0);
df = (file->file[2] << 8) | (file->file[3] << 0);
ef = (file->file[4] << 8) | (file->file[5] << 0);
if (mf != 0x3f00)
continue;
/*
* 8.18: "the path '3F007FFF' indicates the relevant
* NAA Application dedicated file;".
*/
if (df == 0x7fff)
df = 0x7f20;
#define DFGSM (0x7f20 << 16)
#define DFTEL (0x7f10 << 16)
switch ((df << 16) | ef) {
case DFGSM | SIM_EFEST_FILEID:
case DFGSM | SIM_EFUST_FILEID: /* aka. EFSST */
case DFGSM | SIM_EFPHASE_FILEID:
case DFGSM | SIM_EFAD_FILEID:
case DFTEL | SIM_EFBDN_FILEID:
case DFTEL | SIM_EFADN_FILEID:
case DFGSM | SIM_EF_CPHS_INFORMATION_FILEID:
reinit_naa = TRUE;
break;
}
}
/* Flush cached content for affected files */
if (full_file_change)
sim_fs_cache_flush(sim->simfs);
else {
for (l = file_list; l; l = l->next) {
struct stk_file *file = l->data;
int id = (file->file[file->len - 2] << 8) |
(file->file[file->len - 1] << 0);
sim_file_changed_flush(sim, id);
}
}
if (reinit_naa) {
sim->state = OFONO_SIM_STATE_RESETTING;
__ofono_modem_sim_reset(__ofono_atom_get_modem(sim->atom));
/* Force the sim state out of READY */
sim_free_main_state(sim);
call_state_watches(sim);
}
/*
* Notify the subscribers of files that have changed and who
* haven't unsubsribed during the SIM state change.
*/
if (full_file_change)
sim_fs_notify_file_watches(sim->simfs, -1);
else {
for (l = file_list; l; l = l->next) {
struct stk_file *file = l->data;
int id = (file->file[file->len - 2] << 8) |
(file->file[file->len - 1] << 0);
sim_fs_notify_file_watches(sim->simfs, id);
}
}
}
[-- Attachment #6: sms.c --]
[-- Type: text/plain, Size: 30021 bytes --]
/*
*
* oFono - Open Source Telephony
*
* Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#define _GNU_SOURCE
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <glib.h>
#include <ofono/log.h>
#include <ofono/modem.h>
#include <ofono/sms.h>
#include "smsutil.h"
#include "util.h"
#include "vendor.h"
#include "gatchat.h"
#include "gatresult.h"
#include "atmodem.h"
static const char *csca_prefix[] = { "+CSCA:", NULL };
static const char *cgsms_prefix[] = { "+CGSMS:", NULL };
static const char *csms_prefix[] = { "+CSMS:", NULL };
static const char *cmgf_prefix[] = { "+CMGF:", NULL };
static const char *cpms_prefix[] = { "+CPMS:", NULL };
static const char *cnmi_prefix[] = { "+CNMI:", NULL };
static const char *cmgs_prefix[] = { "+CMGS:", NULL };
static const char *cmgl_prefix[] = { "+CMGL:", NULL };
static const char *none_prefix[] = { NULL };
static gboolean set_cmgf(gpointer user_data);
static gboolean set_cpms(gpointer user_data);
static void at_cmgl_set_cpms(struct ofono_sms *sms, int store);
#define MAX_CMGF_RETRIES 10
#define MAX_CPMS_RETRIES 10
static const char *storages[] = {
"SM",
"ME",
"MT",
"SR",
"BM",
};
struct sms_data {
int store;
int incoming;
int retries;
gboolean expect_sr;
gboolean cnma_enabled;
char *cnma_ack_pdu;
int cnma_ack_pdu_len;
guint timeout_source;
GAtChat *chat;
unsigned int vendor;
};
struct cpms_request {
struct ofono_sms *sms;
int store;
int index;
gboolean expect_sr;
};
static void at_csca_set_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct cb_data *cbd = user_data;
ofono_sms_sca_set_cb_t cb = cbd->cb;
struct ofono_error error;
decode_at_error(&error, g_at_result_final_response(result));
cb(&error, cbd->data);
}
static void at_csca_set(struct ofono_sms *sms,
const struct ofono_phone_number *sca,
ofono_sms_sca_set_cb_t cb, void *user_data)
{
struct sms_data *data = ofono_sms_get_data(sms);
struct cb_data *cbd = cb_data_new(cb, user_data);
char buf[64];
snprintf(buf, sizeof(buf), "AT+CSCA=\"%s\",%d", sca->number, sca->type);
if (g_at_chat_send(data->chat, buf, csca_prefix,
at_csca_set_cb, cbd, g_free) > 0)
return;
g_free(cbd);
CALLBACK_WITH_FAILURE(cb, user_data);
}
static void at_csca_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct cb_data *cbd = user_data;
GAtResultIter iter;
ofono_sms_sca_query_cb_t cb = cbd->cb;
struct ofono_error error;
struct ofono_phone_number sca;
const char *number;
decode_at_error(&error, g_at_result_final_response(result));
if (!ok) {
cb(&error, NULL, cbd->data);
return;
}
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "+CSCA:"))
goto err;
if (!g_at_result_iter_next_string(&iter, &number))
goto err;
if (number[0] == '+') {
number = number + 1;
sca.type = 145;
} else {
sca.type = 129;
}
strncpy(sca.number, number, OFONO_MAX_PHONE_NUMBER_LENGTH);
sca.number[OFONO_MAX_PHONE_NUMBER_LENGTH] = '\0';
g_at_result_iter_next_number(&iter, &sca.type);
DBG("csca_query_cb: %s, %d", sca.number, sca.type);
cb(&error, &sca, cbd->data);
return;
err:
CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
}
static void at_csca_query(struct ofono_sms *sms, ofono_sms_sca_query_cb_t cb,
void *user_data)
{
struct sms_data *data = ofono_sms_get_data(sms);
struct cb_data *cbd = cb_data_new(cb, user_data);
if (g_at_chat_send(data->chat, "AT+CSCA?", csca_prefix,
at_csca_query_cb, cbd, g_free) > 0)
return;
g_free(cbd);
CALLBACK_WITH_FAILURE(cb, NULL, user_data);
}
static void at_cmgs_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct cb_data *cbd = user_data;
GAtResultIter iter;
ofono_sms_submit_cb_t cb = cbd->cb;
struct ofono_error error;
int mr;
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, "+CMGS:"))
goto err;
if (!g_at_result_iter_next_number(&iter, &mr))
goto err;
DBG("Got MR: %d", mr);
cb(&error, mr, cbd->data);
return;
err:
CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
}
static void at_cmgs(struct ofono_sms *sms, const unsigned char *pdu,
int pdu_len, int tpdu_len, int mms,
ofono_sms_submit_cb_t cb, void *user_data)
{
struct sms_data *data = ofono_sms_get_data(sms);
struct cb_data *cbd = cb_data_new(cb, user_data);
char buf[512];
int len;
if (mms) {
snprintf(buf, sizeof(buf), "AT+CMMS=%d", mms);
g_at_chat_send(data->chat, buf, none_prefix,
NULL, NULL, NULL);
}
len = snprintf(buf, sizeof(buf), "AT+CMGS=%d\r", tpdu_len);
encode_hex_own_buf(pdu, pdu_len, 0, buf+len);
if (g_at_chat_send(data->chat, buf, cmgs_prefix,
at_cmgs_cb, cbd, g_free) > 0)
return;
g_free(cbd);
CALLBACK_WITH_FAILURE(cb, -1, user_data);
}
static void at_cgsms_set_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct cb_data *cbd = user_data;
ofono_sms_sca_set_cb_t cb = cbd->cb;
struct ofono_error error;
decode_at_error(&error, g_at_result_final_response(result));
cb(&error, cbd->data);
}
static void at_cgsms_set(struct ofono_sms *sms, int bearer,
ofono_sms_bearer_set_cb_t cb, void *user_data)
{
struct sms_data *data = ofono_sms_get_data(sms);
struct cb_data *cbd = cb_data_new(cb, user_data);
char buf[64];
snprintf(buf, sizeof(buf), "AT+CGSMS=%d", bearer);
if (g_at_chat_send(data->chat, buf, none_prefix,
at_cgsms_set_cb, cbd, g_free) > 0)
return;
g_free(cbd);
CALLBACK_WITH_FAILURE(cb, user_data);
}
static void at_cgsms_query_cb(gboolean ok, GAtResult *result,
gpointer user_data)
{
struct cb_data *cbd = user_data;
ofono_sms_bearer_query_cb_t cb = cbd->cb;
struct ofono_error error;
GAtResultIter iter;
int bearer;
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, "+CGSMS:"))
goto err;
g_at_result_iter_next_number(&iter, &bearer);
cb(&error, bearer, cbd->data);
return;
err:
CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
}
static void at_cgsms_query(struct ofono_sms *sms,
ofono_sms_bearer_query_cb_t cb, void *user_data)
{
struct sms_data *data = ofono_sms_get_data(sms);
struct cb_data *cbd = cb_data_new(cb, user_data);
if (g_at_chat_send(data->chat, "AT+CGSMS?", cgsms_prefix,
at_cgsms_query_cb, cbd, g_free) > 0)
return;
g_free(cbd);
CALLBACK_WITH_FAILURE(cb, -1, user_data);
}
static void at_cnma_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
if (!ok)
ofono_error("CNMA acknowledgement failed: "
"Further SMS reception is not guaranteed");
}
static gboolean at_parse_cmt(GAtResult *result, const char **pdu, int *pdulen)
{
GAtResultIter iter;
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "+CMT:"))
return FALSE;
if (!g_at_result_iter_skip_next(&iter))
return FALSE;
if (!g_at_result_iter_next_number(&iter, pdulen))
return FALSE;
*pdu = g_at_result_pdu(result);
return TRUE;
}
static inline void at_ack_delivery(struct ofono_sms *sms)
{
struct sms_data *data = ofono_sms_get_data(sms);
char buf[256];
DBG("");
/* We must acknowledge the PDU using CNMA */
if (data->cnma_ack_pdu)
snprintf(buf, sizeof(buf), "AT+CNMA=1,%d\r%s",
data->cnma_ack_pdu_len, data->cnma_ack_pdu);
else /* Should be a safe fallback */
snprintf(buf, sizeof(buf), "AT+CNMA=0");
g_at_chat_send(data->chat, buf, none_prefix, at_cnma_cb, NULL, NULL);
}
static void at_cds_notify(GAtResult *result, gpointer user_data)
{
struct ofono_sms *sms = user_data;
struct sms_data *data = ofono_sms_get_data(sms);
long pdu_len;
int tpdu_len;
const char *hexpdu;
unsigned char pdu[176];
GAtResultIter iter;
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "+CDS:"))
goto err;
/*
* Quirk for ZTE firmware which is not compliant with 27.005
* The +CDS syntax used by ZTE is including a comma before the length
* +CDS: ,<length><CR><LF><pdu>
* As a result, we need to skip this omitted subparameter
*/
if (data->vendor == OFONO_VENDOR_ZTE)
g_at_result_iter_skip_next(&iter);
if (!g_at_result_iter_next_number(&iter, &tpdu_len))
goto err;
hexpdu = g_at_result_pdu(result);
if (strlen(hexpdu) > sizeof(pdu) * 2) {
ofono_error("Bad PDU length in CDS notification");
return;
}
DBG("Got new Status-Report PDU via CDS: %s, %d", hexpdu, tpdu_len);
/* Decode pdu and notify about new SMS status report */
decode_hex_own_buf(hexpdu, -1, &pdu_len, 0, pdu);
ofono_sms_status_notify(sms, pdu, pdu_len, tpdu_len);
if (data->cnma_enabled && data->vendor != OFONO_VENDOR_TELIT)
at_ack_delivery(sms);
return;
err:
ofono_error("Unable to parse CDS notification");
}
static void at_cmt_notify(GAtResult *result, gpointer user_data)
{
struct ofono_sms *sms = user_data;
struct sms_data *data = ofono_sms_get_data(sms);
const char *hexpdu;
long pdu_len;
int tpdu_len;
unsigned char pdu[176];
if (!at_parse_cmt(result, &hexpdu, &tpdu_len)) {
ofono_error("Unable to parse CMT notification");
return;
}
if (strlen(hexpdu) > sizeof(pdu) * 2) {
ofono_error("Bad PDU length in CMT notification");
return;
}
DBG("Got new SMS Deliver PDU via CMT: %s, %d", hexpdu, tpdu_len);
decode_hex_own_buf(hexpdu, -1, &pdu_len, 0, pdu);
ofono_sms_deliver_notify(sms, pdu, pdu_len, tpdu_len);
if (data->vendor != OFONO_VENDOR_SIMCOM &&
data->vendor != OFONO_VENDOR_TELIT)
at_ack_delivery(sms);
}
static void at_cmgr_notify(GAtResult *result, gpointer user_data)
{
struct ofono_sms *sms = user_data;
struct sms_data *data = ofono_sms_get_data(sms);
GAtResultIter iter;
const char *hexpdu;
unsigned char pdu[176];
long pdu_len;
int tpdu_len;
DBG("");
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "+CMGR:"))
goto err;
if (!g_at_result_iter_skip_next(&iter))
goto err;
if (!g_at_result_iter_skip_next(&iter))
goto err;
if (!g_at_result_iter_next_number(&iter, &tpdu_len))
goto err;
hexpdu = g_at_result_pdu(result);
if (strlen(hexpdu) > sizeof(pdu) * 2)
goto err;
DBG("Got PDU: %s, with len: %d", hexpdu, tpdu_len);
decode_hex_own_buf(hexpdu, -1, &pdu_len, 0, pdu);
if (data->expect_sr)
ofono_sms_status_notify(sms, pdu, pdu_len, tpdu_len);
else
ofono_sms_deliver_notify(sms, pdu, pdu_len, tpdu_len);
return;
err:
ofono_error("Unable to parse CMGR response");
}
static void at_cmgr_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
if (!ok)
ofono_error("Received a CMTI indication but CMGR failed!");
}
static void at_cmgd_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
if (!ok)
ofono_error("Unable to delete received SMS");
}
static void at_cmgr_cpms_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct cpms_request *req = user_data;
struct ofono_sms *sms = req->sms;
struct sms_data *data = ofono_sms_get_data(sms);
char buf[128];
if (!ok) {
ofono_error("Received CMTI/CDSI, but CPMS request failed");
return;
}
data->store = req->store;
data->expect_sr = req->expect_sr;
snprintf(buf, sizeof(buf), "AT+CMGR=%d", req->index);
g_at_chat_send(data->chat, buf, none_prefix, at_cmgr_cb, NULL, NULL);
/* We don't buffer SMS on the SIM/ME, send along a CMGD as well */
snprintf(buf, sizeof(buf), "AT+CMGD=%d", req->index);
g_at_chat_send(data->chat, buf, none_prefix, at_cmgd_cb, NULL, NULL);
}
static void at_send_cmgr_cpms(struct ofono_sms *sms, int store, int index,
gboolean expect_sr)
{
struct sms_data *data = ofono_sms_get_data(sms);
if (store == data->store) {
struct cpms_request req;
req.sms = sms;
req.store = store;
req.index = index;
req.expect_sr = expect_sr;
at_cmgr_cpms_cb(TRUE, NULL, &req);
} else {
char buf[128];
const char *incoming = storages[data->incoming];
struct cpms_request *req = g_new(struct cpms_request, 1);
req->sms = sms;
req->store = store;
req->index = index;
req->expect_sr = expect_sr;
snprintf(buf, sizeof(buf), "AT+CPMS=\"%s\",\"%s\",\"%s\"",
storages[store], storages[store], incoming);
g_at_chat_send(data->chat, buf, cpms_prefix, at_cmgr_cpms_cb,
req, g_free);
}
}
static void at_cmti_notify(GAtResult *result, gpointer user_data)
{
struct ofono_sms *sms = user_data;
enum at_util_sms_store store;
int index;
if (at_util_parse_sms_index_delivery(result, "+CMTI:",
&store, &index) == FALSE)
goto error;
if (store != AT_UTIL_SMS_STORE_SM && store != AT_UTIL_SMS_STORE_ME)
goto error;
DBG("Got a CMTI indication at %s, index: %d", storages[store], index);
at_send_cmgr_cpms(sms, store, index, FALSE);
return;
error:
ofono_error("Unable to parse CMTI notification");
}
static void at_cdsi_notify(GAtResult *result, gpointer user_data)
{
struct ofono_sms *sms = user_data;
enum at_util_sms_store store;
int index;
if (at_util_parse_sms_index_delivery(result, "+CDSI:",
&store, &index) == FALSE)
goto error;
/* Some modems actually store status reports in SM, and not SR */
if (store != AT_UTIL_SMS_STORE_SR && store != AT_UTIL_SMS_STORE_SM &&
store != AT_UTIL_SMS_STORE_ME)
goto error;
DBG("Got a CDSI indication at %s, index: %d", storages[store], index);
at_send_cmgr_cpms(sms, store, index, TRUE);
return;
error:
ofono_error("Unable to parse CDSI notification");
}
static void at_cmgl_done(struct ofono_sms *sms)
{
struct sms_data *data = ofono_sms_get_data(sms);
DBG("");
if (data->incoming == AT_UTIL_SMS_STORE_MT &&
data->store == AT_UTIL_SMS_STORE_ME) {
at_cmgl_set_cpms(sms, AT_UTIL_SMS_STORE_SM);
return;
}
g_at_chat_register(data->chat, "+CMTI:", at_cmti_notify, FALSE,
sms, NULL);
g_at_chat_register(data->chat, "+CMT:", at_cmt_notify, TRUE,
sms, NULL);
g_at_chat_register(data->chat, "+CDS:", at_cds_notify, TRUE,
sms, NULL);
g_at_chat_register(data->chat, "+CDSI:", at_cdsi_notify, FALSE,
sms, NULL);
/* We treat CMGR just like a notification */
g_at_chat_register(data->chat, "+CMGR:", at_cmgr_notify, TRUE,
sms, NULL);
}
static void at_cmgl_notify(GAtResult *result, gpointer user_data)
{
struct ofono_sms *sms = user_data;
struct sms_data *data = ofono_sms_get_data(sms);
GAtResultIter iter;
const char *hexpdu;
unsigned char pdu[176];
long pdu_len;
int tpdu_len;
int index;
int status;
char buf[16];
DBG("");
g_at_result_iter_init(&iter, result);
while (g_at_result_iter_next(&iter, "+CMGL:")) {
if (!g_at_result_iter_next_number(&iter, &index))
goto err;
if (!g_at_result_iter_next_number(&iter, &status))
goto err;
if (!g_at_result_iter_skip_next(&iter))
goto err;
if (!g_at_result_iter_next_number(&iter, &tpdu_len))
goto err;
/* Only MT messages */
if (status != 0 && status != 1)
continue;
hexpdu = g_at_result_pdu(result);
DBG("Found an old SMS PDU: %s, with len: %d",
hexpdu, tpdu_len);
if (strlen(hexpdu) > sizeof(pdu) * 2)
continue;
decode_hex_own_buf(hexpdu, -1, &pdu_len, 0, pdu);
ofono_sms_deliver_notify(sms, pdu, pdu_len, tpdu_len);
/* We don't buffer SMS on the SIM/ME, send along a CMGD */
snprintf(buf, sizeof(buf), "AT+CMGD=%d", index);
g_at_chat_send(data->chat, buf, none_prefix,
at_cmgd_cb, NULL, NULL);
}
return;
err:
ofono_error("Unable to parse CMGL response");
}
static void at_cmgl_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct ofono_sms *sms = user_data;
if (!ok)
DBG("Initial listing SMS storage failed!");
at_cmgl_done(sms);
}
static void at_cmgl_cpms_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct cpms_request *req = user_data;
struct ofono_sms *sms = req->sms;
struct sms_data *data = ofono_sms_get_data(sms);
if (!ok) {
ofono_error("Initial CPMS request failed");
at_cmgl_done(sms);
return;
}
data->store = req->store;
g_at_chat_send_pdu_listing(data->chat, "AT+CMGL=4", cmgl_prefix,
at_cmgl_notify, at_cmgl_cb, sms, NULL);
}
static void at_cmgl_set_cpms(struct ofono_sms *sms, int store)
{
struct sms_data *data = ofono_sms_get_data(sms);
if (store == data->store) {
struct cpms_request req;
req.sms = sms;
req.store = store;
at_cmgl_cpms_cb(TRUE, NULL, &req);
} else {
char buf[128];
const char *readwrite = storages[store];
const char *incoming = storages[data->incoming];
struct cpms_request *req = g_new(struct cpms_request, 1);
req->sms = sms;
req->store = store;
snprintf(buf, sizeof(buf), "AT+CPMS=\"%s\",\"%s\",\"%s\"",
readwrite, readwrite, incoming);
g_at_chat_send(data->chat, buf, cpms_prefix, at_cmgl_cpms_cb,
req, g_free);
}
}
static void at_sms_initialized(struct ofono_sms *sms)
{
struct sms_data *data = ofono_sms_get_data(sms);
/* Inspect and free the incoming SMS storage */
if (data->incoming == AT_UTIL_SMS_STORE_MT)
at_cmgl_set_cpms(sms, AT_UTIL_SMS_STORE_ME);
else
at_cmgl_set_cpms(sms, data->incoming);
ofono_sms_register(sms);
}
static void at_sms_not_supported(struct ofono_sms *sms)
{
ofono_error("SMS not supported by this modem. If this is in error"
" please submit patches to support this hardware");
ofono_sms_remove(sms);
}
static void at_cnmi_set_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct ofono_sms *sms = user_data;
if (!ok)
return at_sms_not_supported(sms);
at_sms_initialized(sms);
}
static inline char wanted_cnmi(int supported, const char *pref)
{
while (*pref) {
if (supported & (1 << (*pref - '0')))
return *pref;
pref++;
}
return '\0';
}
static inline gboolean append_cnmi_element(char *buf, int *len, int cap,
const char *wanted,
gboolean last)
{
char setting = wanted_cnmi(cap, wanted);
if (!setting)
return FALSE;
buf[*len] = setting;
if (last)
buf[*len + 1] = '\0';
else
buf[*len + 1] = ',';
*len += 2;
return TRUE;
}
static gboolean build_cnmi_string(char *buf, int *cnmi_opts,
struct sms_data *data)
{
const char *mode;
int len = sprintf(buf, "AT+CNMI=");
DBG("");
switch (data->vendor) {
case OFONO_VENDOR_GOBI:
case OFONO_VENDOR_QUALCOMM_MSM:
case OFONO_VENDOR_NOVATEL:
case OFONO_VENDOR_HUAWEI:
case OFONO_VENDOR_ZTE:
case OFONO_VENDOR_SIMCOM:
/* MSM devices advertise support for mode 2, but return an
* error if we attempt to actually use it. */
mode = "1";
break;
default:
/* Sounds like 2 is the sanest mode */
mode = "2310";
break;
}
if (!append_cnmi_element(buf, &len, cnmi_opts[0], mode, FALSE))
return FALSE;
/* Prefer to deliver SMS via +CMT if CNMA is supported */
if (!append_cnmi_element(buf, &len, cnmi_opts[1],
data->cnma_enabled ? "21" : "1", FALSE))
return FALSE;
/* Always deliver CB via +CBM, otherwise don't deliver at all */
if (!append_cnmi_element(buf, &len, cnmi_opts[2], "20", FALSE))
return FALSE;
/*
* Some manufacturers seem to have trouble with delivery via +CDS.
* They report the status report properly, however refuse to +CNMA
* ack it with error "CNMA not expected." However, not acking it
* sends the device into la-la land.
*/
switch (data->vendor) {
case OFONO_VENDOR_NOVATEL:
mode = "20";
break;
default:
mode = "120";
break;
}
/*
* Try to deliver Status-Reports via +CDS, then CDSI or don't
* deliver at all
* */
if (!append_cnmi_element(buf, &len, cnmi_opts[3], mode, FALSE))
return FALSE;
/* Don't care about buffering, 0 seems safer */
if (!append_cnmi_element(buf, &len, cnmi_opts[4], "01", TRUE))
return FALSE;
return TRUE;
}
static void construct_ack_pdu(struct sms_data *d)
{
struct sms ackpdu;
unsigned char pdu[164];
int len;
int tpdu_len;
DBG("");
memset(&ackpdu, 0, sizeof(ackpdu));
ackpdu.type = SMS_TYPE_DELIVER_REPORT_ACK;
if (!sms_encode(&ackpdu, &len, &tpdu_len, pdu))
goto err;
/* Constructing an <ackpdu> according to 27.005 Section 4.6 */
if (len != tpdu_len)
goto err;
d->cnma_ack_pdu = encode_hex(pdu, tpdu_len, 0);
if (d->cnma_ack_pdu == NULL)
goto err;
d->cnma_ack_pdu_len = tpdu_len;
return;
err:
ofono_error("Unable to construct Deliver ACK PDU");
}
static void at_cnmi_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct ofono_sms *sms = user_data;
struct sms_data *data = ofono_sms_get_data(sms);
GAtResultIter iter;
int cnmi_opts[5]; /* See 27.005 Section 3.4.1 */
int opt;
int mode;
gboolean supported = FALSE;
char buf[128];
if (!ok)
goto out;
memset(cnmi_opts, 0, sizeof(cnmi_opts));
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "+CNMI:"))
goto out;
for (opt = 0; opt < 5; opt++) {
int min, max;
if (!g_at_result_iter_open_list(&iter))
goto out;
while (g_at_result_iter_next_range(&iter, &min, &max)) {
for (mode = min; mode <= max; mode++)
cnmi_opts[opt] |= 1 << mode;
}
if (!g_at_result_iter_close_list(&iter))
goto out;
}
if (build_cnmi_string(buf, cnmi_opts, data))
supported = TRUE;
/* support for ack pdu is not working */
switch (data->vendor) {
case OFONO_VENDOR_IFX:
case OFONO_VENDOR_GOBI:
case OFONO_VENDOR_ZTE:
case OFONO_VENDOR_ICERA:
case OFONO_VENDOR_HUAWEI:
case OFONO_VENDOR_NOVATEL:
case OFONO_VENDOR_OPTION_HSO:
goto out;
default:
break;
}
if (data->cnma_enabled)
construct_ack_pdu(data);
out:
if (!supported)
return at_sms_not_supported(sms);
g_at_chat_send(data->chat, buf, cnmi_prefix,
at_cnmi_set_cb, sms, NULL);
}
static void at_query_cnmi(struct ofono_sms *sms)
{
struct sms_data *data = ofono_sms_get_data(sms);
g_at_chat_send(data->chat, "AT+CNMI=?", cnmi_prefix,
at_cnmi_query_cb, sms, NULL);
}
static void at_cpms_set_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct ofono_sms *sms = user_data;
struct sms_data *data = ofono_sms_get_data(sms);
if (ok)
return at_query_cnmi(sms);
data->retries += 1;
if (data->retries == MAX_CPMS_RETRIES) {
ofono_error("Unable to set preferred storage");
return at_sms_not_supported(sms);
}
data->timeout_source = g_timeout_add_seconds(1, set_cpms, sms);
}
static gboolean set_cpms(gpointer user_data)
{
struct ofono_sms *sms = user_data;
struct sms_data *data = ofono_sms_get_data(sms);
const char *store = storages[data->store];
const char *incoming = storages[data->incoming];
char buf[128];
if (data->vendor == OFONO_VENDOR_WAVECOM_Q2XXX)
snprintf(buf, sizeof(buf), "AT+CPMS=\"%s\"", store);
else
snprintf(buf, sizeof(buf), "AT+CPMS=\"%s\",\"%s\",\"%s\"",
store, store, incoming);
g_at_chat_send(data->chat, buf, cpms_prefix,
at_cpms_set_cb, sms, NULL);
data->timeout_source = 0;
return FALSE;
}
static void at_cmgf_set_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct ofono_sms *sms = user_data;
struct sms_data *data = ofono_sms_get_data(sms);
if (ok) {
data->retries = 0;
set_cpms(sms);
return;
}
data->retries += 1;
if (data->retries == MAX_CMGF_RETRIES) {
DBG("Unable to enter PDU mode");
return at_sms_not_supported(sms);
}
data->timeout_source = g_timeout_add_seconds(1, set_cmgf, sms);
}
static gboolean set_cmgf(gpointer user_data)
{
struct ofono_sms *sms = user_data;
struct sms_data *data = ofono_sms_get_data(sms);
g_at_chat_send(data->chat, "AT+CMGF=0", cmgf_prefix,
at_cmgf_set_cb, sms, NULL);
data->timeout_source = 0;
return FALSE;
}
static void at_cpms_query_cb(gboolean ok, GAtResult *result,
gpointer user_data)
{
struct ofono_sms *sms = user_data;
struct sms_data *data = ofono_sms_get_data(sms);
gboolean supported = FALSE;
if (ok) {
int mem = 0, mem_max;
GAtResultIter iter;
const char *store;
gboolean me_supported[3];
gboolean sm_supported[3];
gboolean mt_supported[3];
memset(me_supported, 0, sizeof(me_supported));
memset(sm_supported, 0, sizeof(sm_supported));
memset(mt_supported, 0, sizeof(mt_supported));
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "+CPMS:"))
goto out;
if (data->vendor == OFONO_VENDOR_WAVECOM_Q2XXX) {
/* skip initial `(' */
if (!g_at_result_iter_open_list(&iter))
goto out;
/*
* Wavecom Q2 replies: +CPMS: (("SM","BM","SR"),("SM"))
* This reply is broken according to 3GPP TS 07.05.
*/
mem_max = 2;
} else
mem_max = 3;
for (mem = 0; mem < mem_max; mem++) {
if (!g_at_result_iter_open_list(&iter))
goto out;
while (g_at_result_iter_next_string(&iter, &store)) {
if (!strcmp(store, "ME"))
me_supported[mem] = TRUE;
else if (!strcmp(store, "SM"))
sm_supported[mem] = TRUE;
else if (!strcmp(store, "MT"))
mt_supported[mem] = TRUE;
}
if (!g_at_result_iter_close_list(&iter))
goto out;
}
if (data->vendor != OFONO_VENDOR_WAVECOM_Q2XXX &&
!sm_supported[2] && !me_supported[2]
&& !mt_supported[2])
goto out;
if (sm_supported[0] && sm_supported[1]) {
supported = TRUE;
data->store = AT_UTIL_SMS_STORE_SM;
}
if (me_supported[0] && me_supported[1]) {
supported = TRUE;
data->store = AT_UTIL_SMS_STORE_ME;
}
/* This seems to be a special case, where the modem will
* pick & route the SMS to any of the storages supported by
* mem1
*/
if (mt_supported[2] && (sm_supported[0] || me_supported[0]))
data->incoming = AT_UTIL_SMS_STORE_MT;
if (sm_supported[2])
data->incoming = AT_UTIL_SMS_STORE_SM;
if (me_supported[2])
data->incoming = AT_UTIL_SMS_STORE_ME;
}
out:
if (!supported)
return at_sms_not_supported(sms);
set_cmgf(sms);
}
static void at_cmgf_query_cb(gboolean ok, GAtResult *result,
gpointer user_data)
{
struct ofono_sms *sms = user_data;
struct sms_data *data = ofono_sms_get_data(sms);
gboolean supported = FALSE;
if (ok) {
GAtResultIter iter;
int mode;
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "+CMGF:"))
goto out;
if (!g_at_result_iter_open_list(&iter))
goto out;
/* Look for mode 0 (PDU mode) */
while (g_at_result_iter_next_number(&iter, &mode))
if (mode == 0)
supported = TRUE;
}
out:
if (!supported)
return at_sms_not_supported(sms);
g_at_chat_send(data->chat, "AT+CPMS=?", cpms_prefix,
at_cpms_query_cb, sms, NULL);
}
static void at_csms_status_cb(gboolean ok, GAtResult *result,
gpointer user_data)
{
struct ofono_sms *sms = user_data;
struct sms_data *data = ofono_sms_get_data(sms);
gboolean supported = FALSE;
if (ok) {
GAtResultIter iter;
int service;
int mt;
int mo;
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "+CSMS:"))
goto out;
switch (data->vendor) {
case OFONO_VENDOR_HUAWEI:
case OFONO_VENDOR_NOVATEL:
g_at_result_iter_skip_next(&iter);
service = 0;
break;
default:
if (!g_at_result_iter_next_number(&iter, &service))
goto out;
break;
}
if (!g_at_result_iter_next_number(&iter, &mt))
goto out;
if (!g_at_result_iter_next_number(&iter, &mo))
goto out;
if (service == 1)
data->cnma_enabled = TRUE;
if (mt == 1 && mo == 1)
supported = TRUE;
}
out:
if (!supported)
return at_sms_not_supported(sms);
/* Now query supported text format */
g_at_chat_send(data->chat, "AT+CMGF=?", cmgf_prefix,
at_cmgf_query_cb, sms, NULL);
}
static void at_csms_set_cb(gboolean ok, GAtResult *result,
gpointer user_data)
{
struct ofono_sms *sms = user_data;
struct sms_data *data = ofono_sms_get_data(sms);
g_at_chat_send(data->chat, "AT+CSMS?", csms_prefix,
at_csms_status_cb, sms, NULL);
}
static void at_csms_query_cb(gboolean ok, GAtResult *result,
gpointer user_data)
{
struct ofono_sms *sms = user_data;
struct sms_data *data = ofono_sms_get_data(sms);
gboolean cnma_supported = FALSE;
GAtResultIter iter;
int status_min, status_max;
char buf[128];
if (!ok)
return at_sms_not_supported(sms);
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "+CSMS:"))
goto out;
if (!g_at_result_iter_open_list(&iter))
goto out;
while (g_at_result_iter_next_range(&iter, &status_min, &status_max))
if (status_min <= 1 && 1 <= status_max)
cnma_supported = TRUE;
DBG("CSMS query parsed successfully");
out:
snprintf(buf, sizeof(buf), "AT+CSMS=%d", cnma_supported ? 1 : 0);
g_at_chat_send(data->chat, buf, csms_prefix,
at_csms_set_cb, sms, NULL);
}
static int at_sms_probe(struct ofono_sms *sms, unsigned int vendor,
void *user)
{
GAtChat *chat = user;
struct sms_data *data;
data = g_new0(struct sms_data, 1);
data->chat = g_at_chat_clone(chat);
data->vendor = vendor;
ofono_sms_set_data(sms, data);
g_at_chat_send(data->chat, "AT+CSMS=?", csms_prefix,
at_csms_query_cb, sms, NULL);
return 0;
}
static void at_sms_remove(struct ofono_sms *sms)
{
struct sms_data *data = ofono_sms_get_data(sms);
g_free(data->cnma_ack_pdu);
if (data->timeout_source > 0)
g_source_remove(data->timeout_source);
g_at_chat_unref(data->chat);
g_free(data);
ofono_sms_set_data(sms, NULL);
}
static struct ofono_sms_driver driver = {
.name = "atmodem",
.probe = at_sms_probe,
.remove = at_sms_remove,
.sca_query = at_csca_query,
.sca_set = at_csca_set,
.submit = at_cmgs,
.bearer_query = at_cgsms_query,
.bearer_set = at_cgsms_set,
};
void at_sms_init(void)
{
ofono_sms_driver_register(&driver);
}
void at_sms_exit(void)
{
ofono_sms_driver_unregister(&driver);
}
[-- Attachment #7: t3.txt --]
[-- Type: text/plain, Size: 51166 bytes --]
ofonod[6526]: oFono version 1.16
ofonod[6526]: Can't enable deferred setup: Protocol not available (92)
ofonod[6526]: src/plugin.c:__ofono_plugin_init()
ofonod[6526]: plugins/push-notification.c:push_notification_init()
ofonod[6526]: plugins/smart-messaging.c:smart_messaging_init()
ofonod[6526]: src/cdma-provision.c:ofono_cdma_provision_driver_register() driver: 0x8198420 name: CDMA provisioning
ofonod[6526]: src/gprs-provision.c:ofono_gprs_provision_driver_register() driver: 0x81983e0 name: Provisioning
ofonod[6526]: plugins/dun_gw_bluez5.c:dun_gw_init()
ofonod[6526]: src/handsfree-audio.c:ofono_handsfree_card_driver_register() driver: 0x8198260
ofonod[6526]: src/modem.c:ofono_modem_driver_register() driver: 0x81981e0, name: ublox
ofonod[6526]: src/modem.c:ofono_modem_driver_register() driver: 0x8198180, name: quectel
ofonod[6526]: plugins/he910.c:he910_init()
ofonod[6526]: src/modem.c:ofono_modem_driver_register() driver: 0x8198120, name: he910
ofonod[6526]: plugins/connman.c:connman_init()
ofonod[6526]: src/private-network.c:ofono_private_network_driver_register() driver: 0x81980e0, name: ConnMan Private Network
ofonod[6526]: src/modem.c:ofono_modem_driver_register() driver: 0x8198080, name: sim900
ofonod[6526]: src/modem.c:ofono_modem_driver_register() driver: 0x8198020, name: samsung
ofonod[6526]: src/modem.c:ofono_modem_driver_register() driver: 0x8197fc0, name: speedupcdma
ofonod[6526]: src/modem.c:ofono_modem_driver_register() driver: 0x8197f60, name: speedup
ofonod[6526]: src/modem.c:ofono_modem_driver_register() driver: 0x8197f00, name: alcatel
ofonod[6526]: src/modem.c:ofono_modem_driver_register() driver: 0x8197ea0, name: icera
ofonod[6526]: src/modem.c:ofono_modem_driver_register() driver: 0x8197e40, name: linktop
ofonod[6526]: src/modem.c:ofono_modem_driver_register() driver: 0x8197de0, name: nokiacdma
ofonod[6526]: src/modem.c:ofono_modem_driver_register() driver: 0x8197d80, name: nokia
ofonod[6526]: src/modem.c:ofono_modem_driver_register() driver: 0x8197d20, name: tc65
ofonod[6526]: src/modem.c:ofono_modem_driver_register() driver: 0x8197c80, name: ste
ofonod[6526]: src/modem.c:ofono_modem_driver_register() driver: 0x8197c20, name: ifx
ofonod[6526]: src/modem.c:ofono_modem_driver_register() driver: 0x8197bc0, name: palmpre
ofonod[6526]: src/modem.c:ofono_modem_driver_register() driver: 0x8197b60, name: novatel
ofonod[6526]: src/modem.c:ofono_modem_driver_register() driver: 0x8197b00, name: sierra
ofonod[6526]: src/modem.c:ofono_modem_driver_register() driver: 0x8197a80, name: huawei
ofonod[6526]: src/modem.c:ofono_modem_driver_register() driver: 0x8197a20, name: zte
ofonod[6526]: src/modem.c:ofono_modem_driver_register() driver: 0x81979c0, name: hso
ofonod[6526]: src/modem.c:ofono_modem_driver_register() driver: 0x8197960, name: mbm
ofonod[6526]: src/modem.c:ofono_modem_driver_register() driver: 0x8197900, name: calypso
ofonod[6526]: src/modem.c:ofono_modem_driver_register() driver: 0x81978a0, name: wavecom
ofonod[6526]: src/modem.c:ofono_modem_driver_register() driver: 0x8197840, name: g1
ofonod[6526]: src/cdma-voicecall.c:ofono_cdma_voicecall_driver_register() driver: 0x81977c0, name: cdmamodem
ofonod[6526]: src/modem.c:ofono_devinfo_driver_register() driver: 0x81977e0, name: cdmamodem
ofonod[6526]: src/cdma-connman.c:ofono_cdma_connman_driver_register() driver: 0x8197804, name: cdmamodem
ofonod[6526]: src/modem.c:ofono_modem_driver_register() driver: 0x8197720, name: phonesim
ofonod[6526]: src/modem.c:ofono_modem_driver_register() driver: 0x8197760, name: localhfp
ofonod[6526]: src/gprs.c:ofono_gprs_context_driver_register() driver: 0x81976fc, name: phonesim
ofonod[6526]: src/ctm.c:ofono_ctm_driver_register() driver: 0x81976e8, name: phonesim
ofonod[6526]: src/radio-settings.c:ofono_radio_settings_driver_register() driver: 0x81976c0, name: phonesim
ofonod[6526]: plugins/phonesim.c:parse_config() filename /etc/ofono/phonesim.conf
ofonod[6526]: Reading of /etc/ofono/phonesim.conf failed: No such file or directory
ofonod[6526]: src/ussd.c:ofono_ussd_driver_register() driver: 0x8197680, name: speedupmodem
ofonod[6526]: src/voicecall.c:ofono_voicecall_driver_register() driver: 0x8197520, name: hfpmodem
ofonod[6526]: src/modem.c:ofono_devinfo_driver_register() driver: 0x81975dc, name: hfpmodem
ofonod[6526]: src/network.c:ofono_netreg_driver_register() driver: 0x8197580, name: hfpmodem
ofonod[6526]: src/call-volume.c:ofono_call_volume_driver_register() driver: 0x81975b4, name: hfpmodem
ofonod[6526]: src/handsfree.c:ofono_handsfree_driver_register() driver: 0x8197600, name: hfpmodem
ofonod[6526]: src/siri.c:ofono_siri_driver_register() driver: 0x8197630, name: hfpmodem
ofonod[6526]: src/network.c:ofono_netreg_driver_register() driver: 0x8197480, name: dunmodem
ofonod[6526]: src/gprs.c:ofono_gprs_driver_register() driver: 0x81974ac, name: dunmodem
ofonod[6526]: src/voicecall.c:ofono_voicecall_driver_register() driver: 0x81973a0, name: stemodem
ofonod[6526]: src/gprs.c:ofono_gprs_context_driver_register() driver: 0x8197430, name: stemodem
ofonod[6526]: src/radio-settings.c:ofono_radio_settings_driver_register() driver: 0x8197400, name: stemodem
ofonod[6526]: src/voicecall.c:ofono_voicecall_driver_register() driver: 0x8197260, name: ifxmodem
ofonod[6526]: src/audio-settings.c:ofono_audio_settings_driver_register() driver: 0x81972b0, name: ifxmodem
ofonod[6526]: src/radio-settings.c:ofono_radio_settings_driver_register() driver: 0x81972e0, name: ifxmodem
ofonod[6526]: src/gprs.c:ofono_gprs_context_driver_register() driver: 0x8197310, name: ifxmodem
ofonod[6526]: src/stk.c:ofono_stk_driver_register() driver: 0x8197338, name: ifxmodem
ofonod[6526]: src/ctm.c:ofono_ctm_driver_register() driver: 0x8197360, name: ifxmodem
ofonod[6526]: src/gprs.c:ofono_gprs_context_driver_register() driver: 0x81971e0, name: hsomodem
ofonod[6526]: src/radio-settings.c:ofono_radio_settings_driver_register() driver: 0x8197200, name: hsomodem
ofonod[6526]: src/location-reporting.c:ofono_location_reporting_driver_register() driver: 0x8197180, name: telitmodem
ofonod[6526]: src/gprs.c:ofono_gprs_context_driver_register() driver: 0x81970e0, name: mbmmodem
ofonod[6526]: src/stk.c:ofono_stk_driver_register() driver: 0x8197108, name: mbmmodem
ofonod[6526]: src/location-reporting.c:ofono_location_reporting_driver_register() driver: 0x8197128, name: mbmmodem
ofonod[6526]: src/voicecall.c:ofono_voicecall_driver_register() driver: 0x8197040, name: calypsomodem
ofonod[6526]: src/stk.c:ofono_stk_driver_register() driver: 0x8197088, name: calypsomodem
ofonod[6526]: src/ussd.c:ofono_ussd_driver_register() driver: 0x8196f20, name: huaweimodem
ofonod[6526]: src/voicecall.c:ofono_voicecall_driver_register() driver: 0x8196f40, name: huaweimodem
ofonod[6526]: src/audio-settings.c:ofono_audio_settings_driver_register() driver: 0x8196f88, name: huaweimodem
ofonod[6526]: src/radio-settings.c:ofono_radio_settings_driver_register() driver: 0x8196fc0, name: huaweimodem
ofonod[6526]: src/gprs.c:ofono_gprs_context_driver_register() driver: 0x8196f9c, name: huaweimodem
ofonod[6526]: src/cdma-netreg.c:ofono_cdma_netreg_driver_register() driver: 0x8196ff0, name: huaweimodem
ofonod[6526]: src/gprs.c:ofono_gprs_context_driver_register() driver: 0x8196e80, name: iceramodem
ofonod[6526]: src/radio-settings.c:ofono_radio_settings_driver_register() driver: 0x8196ec0, name: iceramodem
ofonod[6526]: src/radio-settings.c:ofono_radio_settings_driver_register() driver: 0x8196e20, name: ztemodem
ofonod[6526]: src/gprs.c:ofono_gprs_context_driver_register() driver: 0x8196de0, name: swmodem
ofonod[6526]: src/radio-settings.c:ofono_radio_settings_driver_register() driver: 0x8196d80, name: nwmodem
ofonod[6526]: src/voicecall.c:ofono_voicecall_driver_register() driver: 0x8196be0, name: atmodem
ofonod[6526]: src/modem.c:ofono_devinfo_driver_register() driver: 0x8196c80, name: atmodem
ofonod[6526]: src/call-barring.c:ofono_call_barring_driver_register() driver: 0x8196c38, name: atmodem
ofonod[6526]: src/call-forwarding.c:ofono_call_forwarding_driver_register() driver: 0x8196960, name: atmodem
ofonod[6526]: src/call-meter.c:ofono_call_meter_driver_register() driver: 0x81969a0, name: atmodem
ofonod[6526]: src/call-settings.c:ofono_call_settings_driver_register() driver: 0x8196860, name: atmodem
ofonod[6526]: src/phonebook.c:ofono_phonebook_driver_register() driver: 0x8196c58, name: atmodem
ofonod[6526]: src/ussd.c:ofono_ussd_driver_register() driver: 0x8196ba4, name: atmodem
ofonod[6526]: src/sms.c:ofono_sms_driver_register() driver: 0x81968e0, name: atmodem
ofonod[6526]: src/sim.c:ofono_sim_driver_register() driver: 0x8196a60, name: atmodem
ofonod[6526]: src/sim.c:ofono_sim_driver_register() driver: 0x8196ac0, name: atmodem-noef
ofonod[6526]: src/stk.c:ofono_stk_driver_register() driver: 0x8196b84, name: atmodem
ofonod[6526]: src/network.c:ofono_netreg_driver_register() driver: 0x8196a00, name: atmodem
ofonod[6526]: src/cbs.c:ofono_cbs_driver_register() driver: 0x8196940, name: atmodem
ofonod[6526]: src/call-volume.c:ofono_call_volume_driver_register() driver: 0x8196ca4, name: atmodem
ofonod[6526]: src/gprs.c:ofono_gprs_driver_register() driver: 0x8196cd4, name: atmodem
ofonod[6526]: src/gprs.c:ofono_gprs_context_driver_register() driver: 0x8196cf8, name: atmodem
ofonod[6526]: src/sim-auth.c:ofono_sim_auth_driver_register() driver: 0x8196d10, name: atmodem
ofonod[6526]: src/gnss.c:ofono_gnss_driver_register() driver: 0x8196d28, name: atmodem
ofonod[6526]: src/modem.c:ofono_modem_driver_register() driver: 0x8196640, name: gobi
ofonod[6526]: src/modem.c:ofono_devinfo_driver_register() driver: 0x8196400, name: qmimodem
ofonod[6526]: src/network.c:ofono_netreg_driver_register() driver: 0x8196480, name: qmimodem
ofonod[6526]: src/voicecall.c:ofono_voicecall_driver_register() driver: 0x8196420, name: qmimodem
ofonod[6526]: src/sim.c:ofono_sim_driver_register() driver: 0x81964c0, name: qmimodem-legacy
ofonod[6526]: src/sim.c:ofono_sim_driver_register() driver: 0x8196520, name: qmimodem
ofonod[6526]: src/sms.c:ofono_sms_driver_register() driver: 0x8196580, name: qmimodem
ofonod[6526]: src/ussd.c:ofono_ussd_driver_register() driver: 0x81965a0, name: qmimodem
ofonod[6526]: src/gprs.c:ofono_gprs_driver_register() driver: 0x81965b4, name: qmimodem
ofonod[6526]: src/gprs.c:ofono_gprs_context_driver_register() driver: 0x81965c8, name: qmimodem
ofonod[6526]: src/radio-settings.c:ofono_radio_settings_driver_register() driver: 0x81965e0, name: qmimodem
ofonod[6526]: src/location-reporting.c:ofono_location_reporting_driver_register() driver: 0x8196608, name: qmimodem
ofonod[6526]: src/modem.c:ofono_modem_driver_register() driver: 0x81963a0, name: u8500
ofonod[6526]: src/modem.c:ofono_devinfo_driver_register() driver: 0x8196380, name: u8500
ofonod[6526]: src/modem.c:ofono_modem_driver_register() driver: 0x8196320, name: n900
ofonod[6526]: src/modem.c:ofono_modem_driver_register() driver: 0x81962c0, name: isiusb
ofonod[6526]: src/modem.c:ofono_devinfo_driver_register() driver: 0x8195fb0, name: isimodem
ofonod[6526]: src/phonebook.c:ofono_phonebook_driver_register() driver: 0x8195fa0, name: isimodem
ofonod[6526]: src/network.c:ofono_netreg_driver_register() driver: 0x8195fe0, name: isimodem
ofonod[6526]: src/voicecall.c:ofono_voicecall_driver_register() driver: 0x8196020, name: isimodem
ofonod[6526]: src/sms.c:ofono_sms_driver_register() driver: 0x8196080, name: isimodem
ofonod[6526]: src/cbs.c:ofono_cbs_driver_register() driver: 0x81960a0, name: isimodem
ofonod[6526]: src/sim.c:ofono_sim_driver_register() driver: 0x81960c0, name: isimodem
ofonod[6526]: src/ussd.c:ofono_ussd_driver_register() driver: 0x8196108, name: isimodem
ofonod[6526]: src/call-forwarding.c:ofono_call_forwarding_driver_register() driver: 0x8196120, name: isimodem
ofonod[6526]: src/call-settings.c:ofono_call_settings_driver_register() driver: 0x8196140, name: isimodem
ofonod[6526]: src/call-barring.c:ofono_call_barring_driver_register() driver: 0x8196170, name: isimodem
ofonod[6526]: src/call-meter.c:ofono_call_meter_driver_register() driver: 0x81961a0, name: isimodem
ofonod[6526]: src/radio-settings.c:ofono_radio_settings_driver_register() driver: 0x81961e0, name: isimodem
ofonod[6526]: src/gprs.c:ofono_gprs_driver_register() driver: 0x8196208, name: isimodem
ofonod[6526]: src/gprs.c:ofono_gprs_context_driver_register() driver: 0x819621c, name: isimodem
ofonod[6526]: src/audio-settings.c:ofono_audio_settings_driver_register() driver: 0x8196234, name: isimodem
ofonod[6526]: src/sim.c:ofono_sim_driver_register() driver: 0x8196240, name: wgmodem2.5
ofonod[6526]: plugins/udevng.c:udev_start()
ofonod[6526]: plugins/udevng.c:enumerate_devices()
ofonod[6526]: plugins/udevng.c:check_usb_device() hub [(null):(null)]
ofonod[6526]: plugins/udevng.c:check_usb_device() usb [0781:5567]
ofonod[6526]: plugins/udevng.c:check_usb_device() usb-storage [(null):(null)]
ofonod[6526]: plugins/udevng.c:check_usb_device() usb [0461:0010]
ofonod[6526]: plugins/udevng.c:check_usb_device() usbhid [(null):(null)]
ofonod[6526]: plugins/udevng.c:check_usb_device() usbhid [(null):(null)]
ofonod[6526]: plugins/udevng.c:check_usb_device() usb [0424:2512]
ofonod[6526]: plugins/udevng.c:check_usb_device() usb [8087:07dc]
ofonod[6526]: plugins/udevng.c:check_usb_device() btusb [(null):(null)]
ofonod[6526]: plugins/udevng.c:check_usb_device() btusb [(null):(null)]
ofonod[6526]: plugins/udevng.c:check_usb_device() usb [1bc7:0021]
ofonod[6526]: plugins/udevng.c:add_device() /sys/devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4.2
ofonod[6526]: plugins/udevng.c:add_device() /sys/devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4.2/1-4.2:1.0/tty/ttyACM0
ofonod[6526]: plugins/udevng.c:add_device() /dev/ttyACM0 (he910) 2/2/1 [00] ==> (null) (null)
ofonod[6526]: plugins/udevng.c:add_device() /sys/devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4.2
ofonod[6526]: plugins/udevng.c:add_device() /sys/devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4.2/1-4.2:1.10/tty/ttyACM5
ofonod[6526]: plugins/udevng.c:add_device() /dev/ttyACM5 (he910) 2/2/1 [0a] ==> (null) (null)
ofonod[6526]: plugins/udevng.c:add_device() /sys/devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4.2
ofonod[6526]: plugins/udevng.c:add_device() /sys/devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4.2/1-4.2:1.12/tty/ttyACM6
ofonod[6526]: plugins/udevng.c:add_device() /dev/ttyACM6 (he910) 2/2/1 [0c] ==> (null) (null)
ofonod[6526]: plugins/udevng.c:add_device() /sys/devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4.2
ofonod[6526]: plugins/udevng.c:add_device() /sys/devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4.2/1-4.2:1.2/tty/ttyACM1
ofonod[6526]: plugins/udevng.c:add_device() /dev/ttyACM1 (he910) 2/2/1 [02] ==> (null) (null)
ofonod[6526]: plugins/udevng.c:add_device() /sys/devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4.2
ofonod[6526]: plugins/udevng.c:add_device() /sys/devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4.2/1-4.2:1.4/tty/ttyACM2
ofonod[6526]: plugins/udevng.c:add_device() /dev/ttyACM2 (he910) 2/2/1 [04] ==> (null) (null)
ofonod[6526]: plugins/udevng.c:add_device() /sys/devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4.2
ofonod[6526]: plugins/udevng.c:add_device() /sys/devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4.2/1-4.2:1.6/tty/ttyACM3
ofonod[6526]: plugins/udevng.c:add_device() /dev/ttyACM3 (he910) 2/2/1 [06] ==> (null) (null)
ofonod[6526]: plugins/udevng.c:add_device() /sys/devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4.2
ofonod[6526]: plugins/udevng.c:add_device() /sys/devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4.2/1-4.2:1.8/tty/ttyACM4
ofonod[6526]: plugins/udevng.c:add_device() /dev/ttyACM4 (he910) 2/2/1 [08] ==> (null) (null)
ofonod[6526]: plugins/udevng.c:check_usb_device() hub [(null):(null)]
ofonod[6526]: plugins/udevng.c:check_usb_device() hub [(null):(null)]
ofonod[6526]: plugins/udevng.c:create_modem() /sys/devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4.2
ofonod[6526]: plugins/udevng.c:create_modem() driver=he910
ofonod[6526]: src/modem.c:ofono_modem_create() name: (null), type: he910
ofonod[6526]: plugins/udevng.c:setup_he910() /sys/devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4.2
ofonod[6526]: plugins/udevng.c:setup_he910() /dev/ttyACM0 2/2/1 00 (null)
ofonod[6526]: plugins/udevng.c:setup_he910() /dev/ttyACM1 2/2/1 02 (null)
ofonod[6526]: plugins/udevng.c:setup_he910() /dev/ttyACM2 2/2/1 04 (null)
ofonod[6526]: plugins/udevng.c:setup_he910() /dev/ttyACM3 2/2/1 06 (null)
ofonod[6526]: plugins/udevng.c:setup_he910() /dev/ttyACM4 2/2/1 08 (null)
ofonod[6526]: plugins/udevng.c:setup_he910() /dev/ttyACM5 2/2/1 0a (null)
ofonod[6526]: plugins/udevng.c:setup_he910() /dev/ttyACM6 2/2/1 0c (null)
ofonod[6526]: plugins/udevng.c:setup_he910() modem=/dev/ttyACM0 aux=/dev/ttyACM3 gps=/dev/ttyACM5
ofonod[6526]: src/modem.c:set_modem_property() modem 0x81baca8 property Modem
ofonod[6526]: src/modem.c:set_modem_property() modem 0x81baca8 property Aux
ofonod[6526]: src/modem.c:set_modem_property() modem 0x81baca8 property GPS
ofonod[6526]: src/modem.c:ofono_modem_register() 0x81baca8
ofonod[6526]: plugins/he910.c:he910_probe() 0x81baca8
ofonod[6526]: src/modem.c:emit_modem_added() 0x81baca8
ofonod[6526]: src/modem.c:call_modemwatches() 0x81baca8 added:1
ofonod[6526]: plugins/hfp_ag_bluez5.c:modem_watch() modem: 0x81baca8, added: 1
ofonod[6526]: plugins/dun_gw_bluez5.c:modem_watch() modem: 0x81baca8, added: 1
ofonod[6526]: plugins/smart-messaging.c:modem_watch() modem: 0x81baca8, added: 1
ofonod[6526]: plugins/push-notification.c:modem_watch() modem: 0x81baca8, added: 1
ofonod[6526]: src/modem.c:ofono_modem_create() name: 357164040824024, type: he910
ofonod[6526]: src/modem.c:set_modem_property() modem 0x81bf130 property Path
ofonod[6526]: src/modem.c:set_modem_property() modem 0x81bf130 property Registered
ofonod[6526]: plugins/udev.c:add_modem() /devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4.2/1-4.2:1.0/tty/ttyACM0 (he910)
ofonod[6526]: src/modem.c:get_modem_property() modem 0x81bf130 property Path
ofonod[6526]: plugins/udev.c:add_modem() /devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4.2/1-4.2:1.10/tty/ttyACM5 (he910)
ofonod[6526]: src/modem.c:get_modem_property() modem 0x81bf130 property Path
ofonod[6526]: plugins/udev.c:add_modem() /devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4.2/1-4.2:1.12/tty/ttyACM6 (he910)
ofonod[6526]: src/modem.c:get_modem_property() modem 0x81bf130 property Path
ofonod[6526]: plugins/udev.c:add_modem() /devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4.2/1-4.2:1.2/tty/ttyACM1 (he910)
ofonod[6526]: src/modem.c:get_modem_property() modem 0x81bf130 property Path
ofonod[6526]: plugins/udev.c:add_modem() /devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4.2/1-4.2:1.4/tty/ttyACM2 (he910)
ofonod[6526]: src/modem.c:get_modem_property() modem 0x81bf130 property Path
ofonod[6526]: plugins/udev.c:add_modem() /devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4.2/1-4.2:1.6/tty/ttyACM3 (he910)
ofonod[6526]: src/modem.c:get_modem_property() modem 0x81bf130 property Path
ofonod[6526]: plugins/udev.c:add_modem() /devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4.2/1-4.2:1.8/tty/ttyACM4 (he910)
ofonod[6526]: plugins/he910.c:he910_enable() 0x81baca8
ofonod[6526]: src/modem.c:get_modem_property() modem 0x81baca8 property Modem
ofonod[6526]: plugins/he910.c:open_device() Modem /dev/ttyACM0
ofonod[6526]: src/modem.c:get_modem_property() modem 0x81baca8 property Aux
ofonod[6526]: plugins/he910.c:open_device() Aux /dev/ttyACM3
ofonod[6526]: Aux: > ATE0 +CMEE=1\r
ofonod[6526]: Aux: < ATE0 +CMEE=1\r
ofonod[6526]: Aux: < \r\nOK\r\n
ofonod[6526]: Aux: > AT#QSS=2\r
ofonod[6526]: Aux: < \r\nOK\r\n
ofonod[6526]: Aux: > AT+CFUN=4\r
ofonod[6526]: Aux: < \r\nOK\r\n
ofonod[6526]: plugins/he910.c:cfun_delay_cb() 0x81baca8
ofonod[6526]: plugins/he910.c:cfun_delay_cb() Wait for AT+CFUN=4 command completion
ofonod[6526]: Aux: > AT+CFUN=1\r
ofonod[6526]: Aux: < \r\nOK\r\n
ofonod[6526]: plugins/he910.c:cfun_enable_cb() 0x81baca8
ofonod[6526]: src/modem.c:modem_change_state() old state: 0, new state: 1
ofonod[6526]: plugins/he910.c:he910_pre_sim() 0x81baca8
ofonod[6526]: Aux: > AT&C0\r
ofonod[6526]: src/sim.c:ofono_sim_add_state_watch() 0x81ba200
ofonod[6526]: src/sim.c:ofono_sim_add_state_watch() 0x81ba200
ofonod[6526]: src/sim.c:ofono_sim_add_state_watch() 0x81ba200
ofonod[6526]: Aux: < \r\nOK\r\n
ofonod[6526]: Aux: > AT+GCAP\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < +GCAP: +CGSM,+DS,+FCLASS,+MS,+ES\r\n\r\nOK\r\n
ofonod[6526]: Aux: > AT$GPSP=?\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < $GPSP: (0,1)\r\n\r\nOK\r\n
ofonod[6526]: Aux: > AT#QSS?\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < #QSS: 2,1\r\n\r\nOK\r\n
ofonod[6526]: plugins/he910.c:qss_query_cb() 0x81baca8
ofonod[6526]: plugins/he910.c:switch_sim_state_status() 0x81baca8, SIM status: 1
ofonod[6526]: Aux: > AT+CGMI\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < Telit\r\n\r\nOK\r\n
ofonod[6526]: Aux: > AT+CRSM=192,12258\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < +CRSM: 144,0,0000000A2FE204000FF04401020000\r\n\r\nOK\r\n
ofonod[6526]: drivers/atmodem/sim.c:at_crsm_info_cb() crsm_info_cb: 90, 00, 15
ofonod[6526]: Aux: > AT+CGMM\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < HE910\r\n\r\nOK\r\n
ofonod[6526]: Aux: > AT+CRSM=176,12258,0,0,10\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < +CRSM: 144,0,98195400502000737177\r\n\r\nOK\r\n
ofonod[6526]: drivers/atmodem/sim.c:at_crsm_read_cb() crsm_read_cb: 90, 00, 10
ofonod[6526]: src/simfs.c:sim_fs_op_read_block_cb() bufoff: 0, dataoff: 0, tocopy: 10
ofonod[6526]: Aux: > AT+CGMR\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < 12.00.004\r\n\r\nOK\r\n
ofonod[6526]: Aux: > AT+CRSM=192,28421\r
ofonod[6526]: Aux: < \r\n+CME ERROR: 4\r\n
ofonod[6526]: Aux: > AT+CGSN\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < 357164040824024\r\n\r\nOK\r\n
ofonod[6526]: Aux: > AT+CRSM=192,12037\r
ofonod[6526]: Aux: < \r\n+CME ERROR: 4\r\n
ofonod[6526]: Aux: > AT+CPIN?\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < +CPIN: READY\r\n\r\nOK\r\n
ofonod[6526]: drivers/atmodem/sim.c:at_cpin_cb() crsm_pin_cb: READY
ofonod[6526]: drivers/atmodem/sim.c:at_pin_retries_query()
ofonod[6526]: Aux: > AT#PCT\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < #PCT: 10\r\n\r\nOK\r\n
ofonod[6526]: drivers/atmodem/sim.c:at_pct_cb() Note: No password required, returning maximum retries:
ofonod[6526]: drivers/atmodem/sim.c:at_pct_cb() retry counter id=1, val=3
ofonod[6526]: drivers/atmodem/sim.c:at_pct_cb() retry counter id=4, val=3
ofonod[6526]: drivers/atmodem/sim.c:at_pct_cb() retry counter id=9, val=10
ofonod[6526]: drivers/atmodem/sim.c:at_pct_cb() retry counter id=11, val=10
ofonod[6526]: Aux: > AT+CRSM=192,28590\r
ofonod[6526]: Aux: < \r\n+CME ERROR: 4\r\n
ofonod[6526]: Aux: > AT+CRSM=192,28589\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < +CRSM: 144,0,000000046FAD040004F04401020000\r\n\r\nOK\r\n
ofonod[6526]: drivers/atmodem/sim.c:at_crsm_info_cb() crsm_info_cb: 90, 00, 15
ofonod[6526]: Aux: > AT+CRSM=176,28589,0,0,4\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < +CRSM: 144,0,00FFFF02\r\n\r\nOK\r\n
ofonod[6526]: drivers/atmodem/sim.c:at_crsm_read_cb() crsm_read_cb: 90, 00, 4
ofonod[6526]: src/simfs.c:sim_fs_op_read_block_cb() bufoff: 0, dataoff: 0, tocopy: 4
ofonod[6526]: Aux: > AT+CRSM=192,28438\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < +CRSM: 148,4\r\n\r\nOK\r\n
ofonod[6526]: Aux: > AT+CRSM=192,28472\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < +CRSM: 144,0,0000000D6F38040014F04401020000\r\n\r\nOK\r\n
ofonod[6526]: drivers/atmodem/sim.c:at_crsm_info_cb() crsm_info_cb: 90, 00, 15
ofonod[6526]: Aux: > AT+CRSM=176,28472,0,0,13\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < +CRSM: 144,0,FF3FFFFF3F003F0F300CF03300\r\n\r\nOK\r\n
ofonod[6526]: drivers/atmodem/sim.c:at_crsm_read_cb() crsm_read_cb: 90, 00, 13
ofonod[6526]: src/simfs.c:sim_fs_op_read_block_cb() bufoff: 0, dataoff: 0, tocopy: 13
ofonod[6526]: Aux: > AT+CRSM=192,28502\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < +CRSM: 148,4\r\n\r\nOK\r\n
ofonod[6526]: Aux: > AT+CIMI\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < 404450520037177\r\n\r\nOK\r\n
ofonod[6526]: drivers/atmodem/sim.c:at_cimi_cb() cimi_cb: 404450520037177
ofonod[6526]: src/modem.c:modem_change_state() old state: 1, new state: 2
ofonod[6526]: src/modem.c:modem_change_state() old state: 2, new state: 3
ofonod[6526]: plugins/he910.c:he910_post_online() 0x81baca8
ofonod[6526]: drivers/atmodem/gprs-context.c:at_gprs_context_probe()
ofonod[6526]: Aux: > AT+CRC=1\r
ofonod[6526]: Aux: < \r\nOK\r\n
ofonod[6526]: Aux: > AT+CLIP=1\r
ofonod[6526]: Aux: < \r\nOK\r\n
ofonod[6526]: Aux: > AT+CDIP=1\r
ofonod[6526]: Aux: < \r\nERROR\r\n
ofonod[6526]: Aux: > AT+CNAP=1\r
ofonod[6526]: Aux: < \r\nERROR\r\n
ofonod[6526]: Aux: > AT+COLP=1\r
ofonod[6526]: Aux: < \r\nOK\r\n
ofonod[6526]: Aux: > AT+CSSN=1,1\r
ofonod[6526]: Aux: < \r\nOK\r\n
ofonod[6526]: Aux: > AT+VTD?\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < 0\r\n\r\nOK\r\n
ofonod[6526]: Aux: > AT+CCWA=1\r
ofonod[6526]: Aux: < \r\nOK\r\n
ofonod[6526]: drivers/atmodem/voicecall.c:at_voicecall_initialized() voicecall_init: registering to notifications
ofonod[6526]: src/sim.c:ofono_sim_add_state_watch() 0x81ba200
ofonod[6526]: plugins/bluez5.c:bt_register_profile() Bluetooth: Registering 0000111f-0000-1000-8000-00805f9b34fb (hfp_ag) profile
ofonod[6526]: Aux: > AT+CREG=?\r
ofonod[6526]: RegisterProfile() replied an error: org.freedesktop.DBus.Error.Spawn.ChildExited, Launch helper exited with unknown return code 1
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < +CREG: (0-2)\r\n\r\nOK\r\n
ofonod[6526]: Aux: > AT+CSCS=GSM\r
ofonod[6526]: Aux: < \r\nOK\r\n
ofonod[6526]: Aux: > AT+CUSD=1\r
ofonod[6526]: Aux: < \r\nOK\r\n
ofonod[6526]: Aux: > AT+CAOC=2\r
ofonod[6526]: Aux: < \r\nOK\r\n
ofonod[6526]: Aux: > AT+CCWE=1\r
ofonod[6526]: Aux: < \r\n+CME ERROR: 3\r\n
ofonod[6526]: Aux: > AT+CGDCONT=?\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < +CGDCONT: (1-5),"IP",,,(0,1),(0,1)\r\n+CGDCONT: (1-5),"IPV6",,,(0,1),(0,1)\r\n+CGDCONT: (1-5),"IPV4V6",,,(0,1),(0,1)\r\n\r\nOK\r\n
ofonod[6526]: Aux: > AT#AUTOATT=1\r
ofonod[6526]: Aux: < \r\nOK\r\n
ofonod[6526]: Aux: > AT+CRSM=192,28480\r
ofonod[6526]: Aux: < \r\n+CRSM: 144,0,0000000E6F40040011F0440102010E\r\n\r\nOK\r\n\r\n#QSS: 2\r\n
ofonod[6526]: drivers/atmodem/sim.c:at_crsm_info_cb() crsm_info_cb: 90, 00, 15
ofonod[6526]: plugins/he910.c:he910_qss_notify() 0x81baca8
ofonod[6526]: plugins/he910.c:switch_sim_state_status() 0x81baca8, SIM status: 2
ofonod[6526]: Aux: > AT+CLCC\r
ofonod[6526]: plugins/udevng.c:remove_device() /sys/devices/virtual/net/3g-wwan
ofonod[6526]: plugins/udev.c:udev_event() subsystem net remove
ofonod[6526]: plugins/udev.c:remove_modem() /devices/virtual/net/3g-wwan
ofonod[6526]: plugins/udev.c:udev_event() subsystem net finished
ofonod[6526]: Aux: < \r\nOK\r\n
ofonod[6526]: Aux: > AT+CREG=2\r
ofonod[6526]: Aux: < \r\nOK\r\n
ofonod[6526]: Aux: > AT+CSCS?\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < +CSCS: "GSM"\r\n\r\nOK\r\n
ofonod[6526]: Aux: > AT+CGREG=?\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < +CGREG: (0-2)\r\n\r\nOK\r\n
ofonod[6526]: Aux: > AT+CRSM=178,28480,1,4,14\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < +CRSM: 145,212,FFFFFFFFFFFFFFFFFFFFFFFFFFFF\r\n\r\nOK\r\n
ofonod[6526]: drivers/atmodem/sim.c:at_crsm_read_cb() crsm_read_cb: 91, d4, 14
ofonod[6526]: Aux: > AT+CIND=?\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < +CIND: (("battchg",(0-5,99)),("signal",(0-7,99)),("service",(0,1)),("sounder",(0,1)),("message",(0,1)),("call",(0,1)),("roam",(0,1)),("smsfull",(0,1)),("rssi",(0-5,99)))\r\n\r\nOK\r\n
ofonod[6526]: Aux: > AT+CGREG=2\r
ofonod[6526]: Aux: < \r\nOK\r\n
ofonod[6526]: Aux: > AT+CGAUTO=0\r
ofonod[6526]: Aux: < \r\nERROR\r\n
ofonod[6526]: Aux: > AT+CGEREP=2,1\r
ofonod[6526]: Aux: < \r\nOK\r\n
ofonod[6526]: plugins/bluez5.c:bt_register_profile() Bluetooth: Registering 00001103-0000-1000-8000-00805f9b34fb (dun_gw) profile
ofonod[6526]: Aux: > AT+CRSM=192,20256\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < +CGEV: NW DEACT IP, "100.66.102.122", 1\r\n\r\n+CGEV: NW DETACH\r\n
ofonod[6526]: drivers/atmodem/gprs-context.c:cgev_notify() cid 1
ofonod[6526]: src/gprs.c:ofono_gprs_detached_notify() /he910_0
ofonod[6526]: RegisterProfile() replied an error: org.freedesktop.DBus.Error.Spawn.ChildExited, Launch helper exited with unknown return code 1
ofonod[6526]: Aux: < \r\n+CME ERROR: 4\r\n
ofonod[6526]: Aux: > AT+CIND=0,0,0,0,0,0,0,0,1\r
ofonod[6526]: Aux: < \r\nOK\r\n
ofonod[6526]: Aux: > AT+CMER=?\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < +CMER: (0-3),(0),(0),(0,2),(0,1)\r\n\r\nOK\r\n
ofonod[6526]: drivers/atmodem/network-registration.c:build_cmer_string()
ofonod[6526]: Aux: > AT#PSNT=1\r
ofonod[6526]: Aux: < \r\nOK\r\n
ofonod[6526]: Aux: > AT+CPSB=1\r
ofonod[6526]: Aux: < \r\nERROR\r\n
ofonod[6526]: Aux: > AT+CRSM=192,28618\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < +CRSM: 148,4\r\n\r\nOK\r\n
ofonod[6526]: Unable to read waiting messages numbers from SIM
ofonod[6526]: Aux: > AT+CMER=3,0,0,2\r
ofonod[6526]: Aux: < \r\nOK\r\n
ofonod[6526]: src/sim.c:ofono_sim_add_spn_watch() 0x81ba200
ofonod[6526]: src/network.c:__ofono_netreg_add_status_watch() 0x81ba9f0
ofonod[6526]: Aux: > AT+CRSM=192,28617\r
ofonod[6526]: Aux: < \r\n+CIEV: rssi,4\r\n
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < +CRSM: 148,4\r\n\r\nOK\r\n
ofonod[6526]: Unable to read mailbox identifies from SIM
ofonod[6526]: Aux: > AT+CREG?\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < +CREG: 2,0\r\n\r\nOK\r\n
ofonod[6526]: src/network.c:ofono_netreg_status_notify() /he910_0 status 0 tech -1
ofonod[6526]: src/network.c:current_operator_callback() 0x81ba9f0, (nil)
ofonod[6526]: src/gprs.c:netreg_status_changed() 0
ofonod[6526]: Aux: > AT+CRSM=192,28433\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < +CRSM: 148,4\r\n\r\nOK\r\n
ofonod[6526]: src/message-waiting.c:mw_cphs_mwis_read_cb() No CPHS MWIS on SIM
ofonod[6526]: Aux: > AT+COPS=0\r
ofonod[6526]: Aux: < \r\nOK\r\n
ofonod[6526]: Aux: > AT+CRSM=192,28619\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < +CREG: 2\r\n
ofonod[6526]: src/network.c:ofono_netreg_status_notify() /he910_0 status 2 tech -1
ofonod[6526]: src/network.c:current_operator_callback() 0x81ba9f0, (nil)
ofonod[6526]: src/gprs.c:netreg_status_changed() 2
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < +CRSM: 148,4\r\n\r\nOK\r\n
ofonod[6526]: Aux: > AT+CREG?\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < +CREG: 2,2\r\n\r\nOK\r\n
ofonod[6526]: src/network.c:ofono_netreg_status_notify() /he910_0 status 2 tech -1
ofonod[6526]: src/network.c:current_operator_callback() 0x81ba9f0, (nil)
ofonod[6526]: src/gprs.c:netreg_status_changed() 2
ofonod[6526]: Aux: > AT+CRSM=192,28613\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < +CRSM: 148,4\r\n\r\nOK\r\n
ofonod[6526]: src/simfs.c:sim_fs_op_read_block() bufoff: 0, seekoff: 39, toread: 17
ofonod[6526]: Aux: > AT+CRSM=192,28621\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < +CRSM: 148,4\r\n\r\nOK\r\n
ofonod[6526]: Aux: > AT+CRSM=192,28437\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < +CRSM: 148,4\r\n\r\nOK\r\n
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < +CREG: 1,"8585","1A22FB4",2\r\n\r\n#PSNT: 3\r\n
ofonod[6526]: src/network.c:ofono_netreg_status_notify() /he910_0 status 1 tech 2
ofonod[6526]: src/gprs.c:netreg_status_changed() 1
ofonod[6526]: Aux: > AT+COPS=3,2\r
ofonod[6526]: Aux: < \r\nOK\r\n
ofonod[6526]: Aux: > AT+COPS?\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < +COPS: 0,2,"40445",2\r\n\r\nOK\r\n
ofonod[6526]: drivers/atmodem/network-registration.c:cops_numeric_cb() Cops numeric got mcc: 404, mnc: 45
ofonod[6526]: Aux: > AT+CIND?\r
ofonod[6526]: Aux: < \r\n+CME ERROR: 14\r\n
ofonod[6526]: src/network.c:signal_strength_callback() Error during signal strength query
ofonod[6526]: Aux: > AT+CGATT=1\r
ofonod[6526]: Aux: < \r\n+CME ERROR: 3\r\n
ofonod[6526]: src/gprs.c:gprs_attach_callback() /he910_0 error = 1
ofonod[6526]: Aux: > AT+COPS=3,0\r
ofonod[6526]: Aux: < \r\nOK\r\n
ofonod[6526]: Aux: > AT+COPS?\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < +COPS: 0,0,"IND airtel",2\r\n\r\nOK\r\n
ofonod[6526]: drivers/atmodem/network-registration.c:cops_cb() cops_cb: IND airtel, 404 45 2
ofonod[6526]: src/network.c:current_operator_callback() 0x81ba9f0, (nil)
ofonod[6526]: src/gprs.c:netreg_status_changed() 1
ofonod[6526]: Aux: > AT+CGREG?\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < +CGREG: 2,0\r\n\r\nOK\r\n
ofonod[6526]: src/gprs.c:registration_status_cb() /he910_0 error 0 status 0
ofonod[6526]: src/gprs.c:ofono_gprs_status_notify() /he910_0 status 0
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < +CGREG: 1,"8585","1A22FB4",2,"01"\r\n
ofonod[6526]: src/gprs.c:ofono_gprs_status_notify() /he910_0 status 1
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < #QSS: 3\r\n
ofonod[6526]: plugins/he910.c:he910_qss_notify() 0x81baca8
ofonod[6526]: plugins/he910.c:switch_sim_state_status() 0x81baca8, SIM status: 3
ofonod[6526]: Aux: > AT+CSCS=?\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < +CSCS: ("GSM","IRA","8859-1","PCCP437","UCS2")\r\n\r\nOK\r\n
ofonod[6526]: Aux: > AT+CSMS=?\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < +CSMS: (0,1)\r\n\r\nOK\r\n
ofonod[6526]: drivers/atmodem/sms.c:at_csms_query_cb() CSMS query parsed successfully
ofonod[6526]: Aux: > AT+CPBS=?\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < +CPBS: ("SM","FD","LD","MC","RC","DC","ME","EN","ON","SD")\r\n\r\nOK\r\n
ofonod[6526]: Aux: > AT+CSMS=1\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < +CSMS: 1,1,1\r\n\r\nOK\r\n
ofonod[6526]: Aux: > AT+CSMS?\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < +CSMS: 1,1,1,1\r\n\r\nOK\r\n
ofonod[6526]: Aux: > AT+CMGF=?\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < +CMGF: (0,1)\r\n\r\nOK\r\n
ofonod[6526]: Aux: > AT+CPMS=?\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < +CPMS: ("SM","ME"),("SM","ME"),("SM","ME")\r\n\r\nOK\r\n
ofonod[6526]: Aux: > AT+CMGF=0\r
ofonod[6526]: Aux: < \r\nOK\r\n
ofonod[6526]: Aux: > AT+CPMS="ME","ME","ME"\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < +CPMS: "ME",0,100,"ME",0,100,"ME",0,100\r\n\r\nOK\r\n
ofonod[6526]: Aux: > AT+CNMI=?\r
ofonod[6526]: Aux: < \r\n
ofonod[6526]: Aux: < +CNMI: (0-3),(0-3),(0,2),(0-2),(0,1)\r\n\r\nOK\r\n
ofonod[6526]: drivers/atmodem/sms.c:build_cnmi_string()
ofonod[6526]: drivers/atmodem/sms.c:construct_ack_pdu()
ofonod[6526]: Aux: > AT+CNMI=2,2,2,1,0\r
ofonod[6526]: Aux: < \r\nOK\r\n
ofonod[6526]: src/network.c:__ofono_netreg_add_status_watch() 0x81ba9f0
ofonod[6526]: src/sms.c:sms_restore_tx_queue()
ofonod[6526]: plugins/push-notification.c:sms_watch() registered
ofonod[6526]: plugins/smart-messaging.c:sms_watch() registered
ofonod[6526]: Aux: > AT+CMGL=4\r
ofonod[6526]: Aux: < \r\nOK\r\n
ofonod[6526]: drivers/atmodem/sms.c:at_cmgl_done()
ofonod[6526]: Aux: > AT+CGSMS=3\r
ofonod[6526]: Aux: < \r\nOK\r\n
ofonod[6526]: plugins/udev.c:udev_event() subsystem net add
ofonod[6526]: plugins/udev.c:udev_event() subsystem net finished
ofonod[6526]: plugins/udev.c:udev_event() subsystem net move
ofonod[6526]: plugins/udev.c:udev_event() subsystem net finished
ofonod[6526]: plugins/udevng.c:check_modem_list()
ofonod[6526]: drivers/atmodem/gprs-context.c:at_gprs_activate_primary() cid 1
ofonod[6526]: src/gprs.c:pri_activate_callback() 0x81bc900
ofonod[6526]: src/gprs.c:pri_activate_callback() Activating context failed with error: Unknown error type
ofonod[6526]: Terminating
ofonod[6526]: src/modem.c:modem_change_state() old state: 3, new state: 0
ofonod[6526]: src/modem.c:flush_atoms()
ofonod[6526]: plugins/push-notification.c:push_notification_cleanup() 0x81bb9f0
ofonod[6526]: plugins/smart-messaging.c:smart_messaging_cleanup() 0x81bad90
ofonod[6526]: src/network.c:__ofono_netreg_remove_status_watch() 0x81ba9f0
ofonod[6526]: src/sms.c:sms_remove() atom: 0x81b0cc0
ofonod[6526]: src/phonebook.c:phonebook_remove() atom: 0x81bc660
ofonod[6526]: src/gprs.c:gprs_context_unregister() 0x81b0a98, 0x81b0958
ofonod[6526]: src/gprs.c:gprs_context_remove() atom: 0x81b0ab8
ofonod[6526]: drivers/atmodem/gprs-context.c:at_gprs_context_remove()
ofonod[6526]: plugins/bluez5.c:bt_unregister_profile() Bluetooth: Unregistering profile /bluetooth/profile/dun_gw
ofonod[6526]: src/gprs.c:gprs_unregister() 0x81b0958
ofonod[6526]: src/network.c:__ofono_netreg_remove_status_watch() 0x81ba9f0
ofonod[6526]: src/gprs.c:gprs_remove() atom: 0x81b09c0
ofonod[6526]: src/message-waiting.c:mw_remove() atom: 0x81bb110
ofonod[6526]: src/call-barring.c:call_barring_remove() atom: 0x81bb088
ofonod[6526]: src/call-meter.c:call_meter_remove() atom: 0x81baf80
ofonod[6526]: src/call-settings.c:call_settings_remove() atom: 0x81baec8
ofonod[6526]: src/call-forwarding.c:call_forwarding_remove() atom: 0x81bae08
ofonod[6526]: src/ussd.c:ussd_remove() atom: 0x81bab68
ofonod[6526]: src/sim.c:ofono_sim_remove_spn_watch() 0x81ba200
ofonod[6526]: src/network.c:netreg_remove() atom: 0x81baa58
ofonod[6526]: src/voicecall.c:voicecall_remove() atom: 0x81ba3b8
ofonod[6526]: src/location-reporting.c:location_reporting_remove() atom: 0x81ba7f0
ofonod[6526]: plugins/bluez5.c:bt_unregister_profile() Bluetooth: Unregistering profile /bluetooth/profile/hfp_ag
ofonod[6526]: src/sim.c:sim_remove() atom: 0x81ba328
ofonod[6526]: src/modem.c:devinfo_remove() atom: 0x81be420
ofonod[6526]: plugins/he910.c:he910_disable() 0x81baca8
ofonod[6526]: Aux: > AT+CFUN=4\r
ofonod[6526]: UnregisterProfile() replied an error: org.freedesktop.DBus.Error.Spawn.ChildExited, Launch helper exited with unknown return code 1
ofonod[6526]: UnregisterProfile() replied an error: org.freedesktop.DBus.Error.Spawn.ChildExited, Launch helper exited with unknown return code 1
ofonod[6526]: Aux: < \r\nOK\r\n
ofonod[6526]: plugins/he910.c:cfun_disable_cb() 0x81baca8
ofonod[6526]: src/modem.c:modem_change_state() old state: 0, new state: 0
ofonod[6526]: src/plugin.c:__ofono_plugin_cleanup()
ofonod[6526]: plugins/push-notification.c:push_notification_exit()
ofonod[6526]: plugins/smart-messaging.c:smart_messaging_exit()
ofonod[6526]: src/cdma-provision.c:ofono_cdma_provision_driver_unregister() driver: 0x8198420 name: CDMA provisioning
ofonod[6526]: src/gprs-provision.c:ofono_gprs_provision_driver_unregister() driver: 0x81983e0 name: Provisioning
ofonod[6526]: plugins/bluez5.c:bt_unregister_profile() Bluetooth: Unregistering profile /bluetooth/profile/dun_gw
ofonod[6526]: src/modem.c:ofono_modem_driver_unregister() driver: 0x81981e0, name: ublox
ofonod[6526]: src/modem.c:ofono_modem_driver_unregister() driver: 0x8198180, name: quectel
ofonod[6526]: src/modem.c:ofono_modem_driver_unregister() driver: 0x8198120, name: he910
ofonod[6526]: src/modem.c:modem_unregister() 0x81baca8
ofonod[6526]: plugins/he910.c:he910_remove() 0x81baca8
ofonod[6526]: src/modem.c:unregister_property() property 0x81ae2c8
ofonod[6526]: src/modem.c:unregister_property() property 0x81bb450
ofonod[6526]: src/modem.c:unregister_property() property 0x81bb2f0
ofonod[6526]: src/modem.c:emit_modem_removed() 0x81baca8
ofonod[6526]: src/modem.c:call_modemwatches() 0x81baca8 added:0
ofonod[6526]: src/private-network.c:ofono_private_network_driver_unregister() driver: 0x81980e0, name: ConnMan Private Network
ofonod[6526]: src/modem.c:ofono_modem_driver_unregister() driver: 0x8198080, name: sim900
ofonod[6526]: src/modem.c:ofono_modem_driver_unregister() driver: 0x8198020, name: samsung
ofonod[6526]: src/modem.c:ofono_modem_driver_unregister() driver: 0x8197fc0, name: speedupcdma
ofonod[6526]: src/modem.c:ofono_modem_driver_unregister() driver: 0x8197f60, name: speedup
ofonod[6526]: src/modem.c:ofono_modem_driver_unregister() driver: 0x8197f00, name: alcatel
ofonod[6526]: src/modem.c:ofono_modem_driver_unregister() driver: 0x8197ea0, name: icera
ofonod[6526]: src/modem.c:ofono_modem_driver_unregister() driver: 0x8197e40, name: linktop
ofonod[6526]: src/modem.c:ofono_modem_driver_unregister() driver: 0x8197de0, name: nokiacdma
ofonod[6526]: src/modem.c:ofono_modem_driver_unregister() driver: 0x8197d80, name: nokia
ofonod[6526]: src/modem.c:ofono_modem_driver_unregister() driver: 0x8197d20, name: tc65
ofonod[6526]: src/modem.c:ofono_modem_driver_unregister() driver: 0x8197c80, name: ste
ofonod[6526]: src/modem.c:ofono_modem_driver_unregister() driver: 0x8197c20, name: ifx
ofonod[6526]: src/modem.c:ofono_modem_driver_unregister() driver: 0x8197bc0, name: palmpre
ofonod[6526]: src/modem.c:ofono_modem_driver_unregister() driver: 0x8197b60, name: novatel
ofonod[6526]: src/modem.c:ofono_modem_driver_unregister() driver: 0x8197b00, name: sierra
ofonod[6526]: src/modem.c:ofono_modem_driver_unregister() driver: 0x8197a80, name: huawei
ofonod[6526]: src/modem.c:ofono_modem_driver_unregister() driver: 0x8197a20, name: zte
ofonod[6526]: src/modem.c:ofono_modem_driver_unregister() driver: 0x81979c0, name: hso
ofonod[6526]: src/modem.c:ofono_modem_driver_unregister() driver: 0x8197960, name: mbm
ofonod[6526]: src/modem.c:ofono_modem_driver_unregister() driver: 0x8197900, name: calypso
ofonod[6526]: src/modem.c:ofono_modem_driver_unregister() driver: 0x81978a0, name: wavecom
ofonod[6526]: src/modem.c:ofono_modem_driver_unregister() driver: 0x8197840, name: g1
ofonod[6526]: src/cdma-voicecall.c:ofono_cdma_voicecall_driver_unregister() driver: 0x81977c0, name: cdmamodem
ofonod[6526]: src/modem.c:ofono_devinfo_driver_unregister() driver: 0x81977e0, name: cdmamodem
ofonod[6526]: src/cdma-connman.c:ofono_cdma_connman_driver_unregister() driver: 0x8197804, name: cdmamodem
ofonod[6526]: src/radio-settings.c:ofono_radio_settings_driver_unregister() driver: 0x81976c0, name: phonesim
ofonod[6526]: src/ctm.c:ofono_ctm_driver_unregister() driver: 0x81976e8, name: phonesim
ofonod[6526]: src/gprs.c:ofono_gprs_context_driver_unregister() driver: 0x81976fc, name: phonesim
ofonod[6526]: src/modem.c:ofono_modem_driver_unregister() driver: 0x8197720, name: phonesim
ofonod[6526]: src/ussd.c:ofono_ussd_driver_unregister() driver: 0x8197680, name: speedupmodem
ofonod[6526]: src/voicecall.c:ofono_voicecall_driver_unregister() driver: 0x8197520, name: hfpmodem
ofonod[6526]: src/modem.c:ofono_devinfo_driver_unregister() driver: 0x81975dc, name: hfpmodem
ofonod[6526]: src/network.c:ofono_netreg_driver_unregister() driver: 0x8197580, name: hfpmodem
ofonod[6526]: src/call-volume.c:ofono_call_volume_driver_unregister() driver: 0x81975b4, name: hfpmodem
ofonod[6526]: src/handsfree.c:ofono_handsfree_driver_unregister() driver: 0x8197600, name: hfpmodem
ofonod[6526]: src/siri.c:ofono_siri_driver_unregister() driver: 0x8197630, name: hfpmodem
ofonod[6526]: src/network.c:ofono_netreg_driver_unregister() driver: 0x8197480, name: dunmodem
ofonod[6526]: src/gprs.c:ofono_gprs_driver_unregister() driver: 0x81974ac, name: dunmodem
ofonod[6526]: src/voicecall.c:ofono_voicecall_driver_unregister() driver: 0x81973a0, name: stemodem
ofonod[6526]: src/gprs.c:ofono_gprs_context_driver_unregister() driver: 0x8197430, name: stemodem
ofonod[6526]: src/radio-settings.c:ofono_radio_settings_driver_unregister() driver: 0x8197400, name: stemodem
ofonod[6526]: src/stk.c:ofono_stk_driver_unregister() driver: 0x8197338, name: ifxmodem
ofonod[6526]: src/gprs.c:ofono_gprs_context_driver_unregister() driver: 0x8197310, name: ifxmodem
ofonod[6526]: src/radio-settings.c:ofono_radio_settings_driver_unregister() driver: 0x81972e0, name: ifxmodem
ofonod[6526]: src/audio-settings.c:ofono_audio_settings_driver_unregister() driver: 0x81972b0, name: ifxmodem
ofonod[6526]: src/voicecall.c:ofono_voicecall_driver_unregister() driver: 0x8197260, name: ifxmodem
ofonod[6526]: src/ctm.c:ofono_ctm_driver_unregister() driver: 0x8197360, name: ifxmodem
ofonod[6526]: src/gprs.c:ofono_gprs_context_driver_unregister() driver: 0x81971e0, name: hsomodem
ofonod[6526]: src/radio-settings.c:ofono_radio_settings_driver_unregister() driver: 0x8197200, name: hsomodem
ofonod[6526]: src/location-reporting.c:ofono_location_reporting_driver_unregister() driver: 0x8197180, name: telitmodem
ofonod[6526]: src/location-reporting.c:ofono_location_reporting_driver_unregister() driver: 0x8197128, name: mbmmodem
ofonod[6526]: src/stk.c:ofono_stk_driver_unregister() driver: 0x8197108, name: mbmmodem
ofonod[6526]: src/gprs.c:ofono_gprs_context_driver_unregister() driver: 0x81970e0, name: mbmmodem
ofonod[6526]: src/stk.c:ofono_stk_driver_unregister() driver: 0x8197088, name: calypsomodem
ofonod[6526]: src/voicecall.c:ofono_voicecall_driver_unregister() driver: 0x8197040, name: calypsomodem
ofonod[6526]: src/cdma-netreg.c:ofono_cdma_netreg_driver_unregister() driver: 0x8196ff0, name: huaweimodem
ofonod[6526]: src/gprs.c:ofono_gprs_context_driver_unregister() driver: 0x8196f9c, name: huaweimodem
ofonod[6526]: src/radio-settings.c:ofono_radio_settings_driver_unregister() driver: 0x8196fc0, name: huaweimodem
ofonod[6526]: src/audio-settings.c:ofono_audio_settings_driver_unregister() driver: 0x8196f88, name: huaweimodem
ofonod[6526]: src/voicecall.c:ofono_voicecall_driver_unregister() driver: 0x8196f40, name: huaweimodem
ofonod[6526]: src/ussd.c:ofono_ussd_driver_unregister() driver: 0x8196f20, name: huaweimodem
ofonod[6526]: src/gprs.c:ofono_gprs_context_driver_unregister() driver: 0x8196e80, name: iceramodem
ofonod[6526]: src/radio-settings.c:ofono_radio_settings_driver_unregister() driver: 0x8196ec0, name: iceramodem
ofonod[6526]: src/radio-settings.c:ofono_radio_settings_driver_unregister() driver: 0x8196e20, name: ztemodem
ofonod[6526]: src/gprs.c:ofono_gprs_context_driver_unregister() driver: 0x8196de0, name: swmodem
ofonod[6526]: src/radio-settings.c:ofono_radio_settings_driver_unregister() driver: 0x8196d80, name: nwmodem
ofonod[6526]: src/sim-auth.c:ofono_sim_auth_driver_unregister() driver: 0x8196d10, name: atmodem
ofonod[6526]: src/stk.c:ofono_stk_driver_unregister() driver: 0x8196b84, name: atmodem
ofonod[6526]: src/sim.c:ofono_sim_driver_unregister() driver: 0x8196a60, name: atmodem
ofonod[6526]: src/sim.c:ofono_sim_driver_unregister() driver: 0x8196ac0, name: atmodem-noef
ofonod[6526]: src/sms.c:ofono_sms_driver_unregister() driver: 0x81968e0, name: atmodem
ofonod[6526]: src/ussd.c:ofono_ussd_driver_unregister() driver: 0x8196ba4, name: atmodem
ofonod[6526]: src/phonebook.c:ofono_phonebook_driver_unregister() driver: 0x8196c58, name: atmodem
ofonod[6526]: src/call-settings.c:ofono_call_settings_driver_unregister() driver: 0x8196860, name: atmodem
ofonod[6526]: src/call-meter.c:ofono_call_meter_driver_unregister() driver: 0x81969a0, name: atmodem
ofonod[6526]: src/call-forwarding.c:ofono_call_forwarding_driver_unregister() driver: 0x8196960, name: atmodem
ofonod[6526]: src/call-barring.c:ofono_call_barring_driver_unregister() driver: 0x8196c38, name: atmodem
ofonod[6526]: src/network.c:ofono_netreg_driver_unregister() driver: 0x8196a00, name: atmodem
ofonod[6526]: src/modem.c:ofono_devinfo_driver_unregister() driver: 0x8196c80, name: atmodem
ofonod[6526]: src/voicecall.c:ofono_voicecall_driver_unregister() driver: 0x8196be0, name: atmodem
ofonod[6526]: src/cbs.c:ofono_cbs_driver_unregister() driver: 0x8196940, name: atmodem
ofonod[6526]: src/call-volume.c:ofono_call_volume_driver_unregister() driver: 0x8196ca4, name: atmodem
ofonod[6526]: src/gprs.c:ofono_gprs_driver_unregister() driver: 0x8196cd4, name: atmodem
ofonod[6526]: src/gprs.c:ofono_gprs_context_driver_unregister() driver: 0x8196cf8, name: atmodem
ofonod[6526]: src/gnss.c:ofono_gnss_driver_unregister() driver: 0x8196d28, name: atmodem
ofonod[6526]: src/modem.c:ofono_modem_driver_unregister() driver: 0x8196640, name: gobi
ofonod[6526]: src/location-reporting.c:ofono_location_reporting_driver_unregister() driver: 0x8196608, name: qmimodem
ofonod[6526]: src/radio-settings.c:ofono_radio_settings_driver_unregister() driver: 0x81965e0, name: qmimodem
ofonod[6526]: src/gprs.c:ofono_gprs_context_driver_unregister() driver: 0x81965c8, name: qmimodem
ofonod[6526]: src/gprs.c:ofono_gprs_driver_unregister() driver: 0x81965b4, name: qmimodem
ofonod[6526]: src/ussd.c:ofono_ussd_driver_unregister() driver: 0x81965a0, name: qmimodem
ofonod[6526]: src/sms.c:ofono_sms_driver_unregister() driver: 0x8196580, name: qmimodem
ofonod[6526]: src/sim.c:ofono_sim_driver_unregister() driver: 0x8196520, name: qmimodem
ofonod[6526]: src/sim.c:ofono_sim_driver_unregister() driver: 0x81964c0, name: qmimodem-legacy
ofonod[6526]: src/voicecall.c:ofono_voicecall_driver_unregister() driver: 0x8196420, name: qmimodem
ofonod[6526]: src/network.c:ofono_netreg_driver_unregister() driver: 0x8196480, name: qmimodem
ofonod[6526]: src/modem.c:ofono_devinfo_driver_unregister() driver: 0x8196400, name: qmimodem
ofonod[6526]: src/modem.c:ofono_devinfo_driver_unregister() driver: 0x8196380, name: u8500
ofonod[6526]: src/modem.c:ofono_modem_driver_unregister() driver: 0x81963a0, name: u8500
ofonod[6526]: src/modem.c:ofono_modem_driver_unregister() driver: 0x8196320, name: n900
ofonod[6526]: src/modem.c:ofono_modem_driver_unregister() driver: 0x81962c0, name: isiusb
ofonod[6526]: src/modem.c:ofono_devinfo_driver_unregister() driver: 0x8195fb0, name: isimodem
ofonod[6526]: src/phonebook.c:ofono_phonebook_driver_unregister() driver: 0x8195fa0, name: isimodem
ofonod[6526]: src/network.c:ofono_netreg_driver_unregister() driver: 0x8195fe0, name: isimodem
ofonod[6526]: src/voicecall.c:ofono_voicecall_driver_unregister() driver: 0x8196020, name: isimodem
ofonod[6526]: src/sms.c:ofono_sms_driver_unregister() driver: 0x8196080, name: isimodem
ofonod[6526]: src/cbs.c:ofono_cbs_driver_unregister() driver: 0x81960a0, name: isimodem
ofonod[6526]: src/sim.c:ofono_sim_driver_unregister() driver: 0x81960c0, name: isimodem
ofonod[6526]: src/ussd.c:ofono_ussd_driver_unregister() driver: 0x8196108, name: isimodem
ofonod[6526]: src/call-forwarding.c:ofono_call_forwarding_driver_unregister() driver: 0x8196120, name: isimodem
ofonod[6526]: src/call-settings.c:ofono_call_settings_driver_unregister() driver: 0x8196140, name: isimodem
ofonod[6526]: src/call-barring.c:ofono_call_barring_driver_unregister() driver: 0x8196170, name: isimodem
ofonod[6526]: src/call-meter.c:ofono_call_meter_driver_unregister() driver: 0x81961a0, name: isimodem
ofonod[6526]: src/radio-settings.c:ofono_radio_settings_driver_unregister() driver: 0x81961e0, name: isimodem
ofonod[6526]: src/gprs.c:ofono_gprs_driver_unregister() driver: 0x8196208, name: isimodem
ofonod[6526]: src/gprs.c:ofono_gprs_context_driver_unregister() driver: 0x819621c, name: isimodem
ofonod[6526]: src/audio-settings.c:ofono_audio_settings_driver_unregister() driver: 0x8196234, name: isimodem
ofonod[6526]: src/sim.c:ofono_sim_driver_unregister() driver: 0x8196240, name: wgmodem2.5
ofonod[6526]: plugins/udevng.c:destroy_modem() /sys/devices/pci0000:00/0000:00:14.0/usb1/1-4/1-4.2
ofonod[6526]: src/modem.c:ofono_modem_remove() 0x81baca8
ofonod[6526]: plugins/udevng.c:destroy_modem() /dev/ttyACM0
ofonod[6526]: plugins/udevng.c:destroy_modem() /dev/ttyACM1
ofonod[6526]: plugins/udevng.c:destroy_modem() /dev/ttyACM2
ofonod[6526]: plugins/udevng.c:destroy_modem() /dev/ttyACM3
ofonod[6526]: plugins/udevng.c:destroy_modem() /dev/ttyACM4
ofonod[6526]: plugins/udevng.c:destroy_modem() /dev/ttyACM5
ofonod[6526]: plugins/udevng.c:destroy_modem() /dev/ttyACM6
ofonod[6526]: src/modem.c:ofono_modem_remove() 0x81bf130
ofonod[6526]: Exit
[-- Attachment #8: ussd.c --]
[-- Type: text/plain, Size: 8330 bytes --]
/*
*
* oFono - Open Source Telephony
*
* Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#define _GNU_SOURCE
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <glib.h>
#include <ofono/log.h>
#include <ofono/modem.h>
#include <ofono/ussd.h>
#include "util.h"
#include "smsutil.h"
#include "vendor.h"
#include "gatchat.h"
#include "gatresult.h"
#include "atmodem.h"
static const char *cusd_prefix[] = { "+CUSD:", NULL };
static const char *none_prefix[] = { NULL };
static const char *cscs_prefix[] = { "+CSCS:", NULL };
struct ussd_data {
GAtChat *chat;
unsigned int vendor;
enum at_util_charset charset;
};
static void read_charset_cb(gboolean ok, GAtResult *result,
gpointer user_data)
{
struct ussd_data *data = user_data;
if (!ok)
return;
at_util_parse_cscs_query(result, &data->charset);
}
static void charset_cb(gboolean ok, GAtResult *result,
gpointer user_data)
{
struct ussd_data *data = user_data;
if (!ok)
return;
g_at_chat_send(data->chat, "AT+CSCS?", cscs_prefix,
read_charset_cb, data, NULL);
}
static const unsigned char *ucs2_gsm_to_packed(const char *content,
long *msg_len,
unsigned char *msg)
{
unsigned char *decoded;
long len;
unsigned char *gsm;
long written;
unsigned char *packed;
unsigned char buf[182 * 2]; /* 182 USSD chars * 2 (UCS2) */
if (strlen(content) > sizeof(buf) * 2) /* Hex, 2 chars / byte */
return NULL;
decoded = decode_hex_own_buf(content, -1, &len, 0, buf);
if (decoded == NULL)
return NULL;
gsm = convert_ucs2_to_gsm(decoded, len, NULL, &written, 0);
if (gsm == NULL)
return NULL;
if (written > 182) {
g_free(gsm);
return NULL;
}
packed = pack_7bit_own_buf(gsm, written, 0, TRUE, msg_len, 0, msg);
g_free(gsm);
return packed;
}
static void cusd_parse(GAtResult *result, struct ofono_ussd *ussd)
{
struct ussd_data *data = ofono_ussd_get_data(ussd);
GAtResultIter iter;
int status;
const char *content;
int dcs;
enum sms_charset charset;
unsigned char msg[160];
const unsigned char *msg_ptr = NULL;
long msg_len;
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "+CUSD:"))
return;
if (!g_at_result_iter_next_number(&iter, &status))
return;
if (!g_at_result_iter_next_string(&iter, &content))
goto out;
if (!g_at_result_iter_next_number(&iter, &dcs))
dcs = 0;
if (!cbs_dcs_decode(dcs, NULL, NULL, &charset, NULL, NULL, NULL)) {
ofono_error("Unsupported USSD data coding scheme (%02x)", dcs);
status = 4; /* Not supported */
goto out;
}
DBG("response charset %d modem charset %d", charset, data->charset);
switch (charset) {
case SMS_CHARSET_7BIT:
switch (data->charset) {
case AT_UTIL_CHARSET_GSM:
msg_ptr = pack_7bit_own_buf((const guint8 *) content,
-1, 0, TRUE, &msg_len,
0, msg);
break;
case AT_UTIL_CHARSET_UTF8:
if (ussd_encode(content, &msg_len, msg) == TRUE)
msg_ptr = msg;
break;
case AT_UTIL_CHARSET_UCS2:
msg_ptr = ucs2_gsm_to_packed(content, &msg_len, msg);
break;
default:
msg_ptr = NULL;
}
break;
case SMS_CHARSET_8BIT:
case SMS_CHARSET_UCS2:
msg_ptr = decode_hex_own_buf(content, -1, &msg_len, 0, msg);
break;
}
DBG("msg ptr %p msg len %ld", msg_ptr, msg_len);
out:
ofono_ussd_notify(ussd, status, dcs, msg_ptr, msg_ptr ? msg_len : 0);
}
static void cusd_request_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct cb_data *cbd = user_data;
ofono_ussd_cb_t cb = cbd->cb;
struct ofono_ussd *ussd = cbd->user;
struct ofono_error error;
decode_at_error(&error, g_at_result_final_response(result));
cb(&error, cbd->data);
cusd_parse(result, ussd);
}
static void at_ussd_request(struct ofono_ussd *ussd, int dcs,
const unsigned char *pdu, int len,
ofono_ussd_cb_t cb, void *user_data)
{
struct ussd_data *data = ofono_ussd_get_data(ussd);
struct cb_data *cbd = cb_data_new(cb, user_data);
char buf[512];
enum sms_charset charset;
cbd->user = ussd;
if (!cbs_dcs_decode(dcs, NULL, NULL, &charset,
NULL, NULL, NULL))
goto error;
if (charset == SMS_CHARSET_7BIT) {
unsigned char unpacked_buf[182];
long written;
unpack_7bit_own_buf(pdu, len, 0, TRUE, sizeof(unpacked_buf),
&written, 0, unpacked_buf);
if (written < 1)
goto error;
snprintf(buf, sizeof(buf), "AT+CUSD=1,\"%.*s\",%d",
(int) written, unpacked_buf, dcs);
} else {
char coded_buf[321];
char *converted = encode_hex_own_buf(pdu, len, 0, coded_buf);
if (converted == NULL)
goto error;
snprintf(buf, sizeof(buf), "AT+CUSD=1,\"%s\",%d",
converted, dcs);
}
if (g_at_chat_send(data->chat, buf, cusd_prefix,
cusd_request_cb, cbd, g_free) > 0)
return;
error:
g_free(cbd);
CALLBACK_WITH_FAILURE(cb, user_data);
}
static void cusd_cancel_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct cb_data *cbd = user_data;
ofono_ussd_cb_t cb = cbd->cb;
struct ussd_data *data = cbd->user;
struct ofono_error error;
decode_at_error(&error, g_at_result_final_response(result));
switch (data->vendor) {
case OFONO_VENDOR_GOBI:
case OFONO_VENDOR_QUALCOMM_MSM:
/* All errors and notifications arrive unexpected and
* thus just reset the state here. This is safer than
* getting stuck in a dead-lock. */
error.type = OFONO_ERROR_TYPE_NO_ERROR;
error.error = 0;
break;
default:
break;
}
cb(&error, cbd->data);
}
static void at_ussd_cancel(struct ofono_ussd *ussd,
ofono_ussd_cb_t cb, void *user_data)
{
struct ussd_data *data = ofono_ussd_get_data(ussd);
struct cb_data *cbd = cb_data_new(cb, user_data);
cbd->user = data;
if (g_at_chat_send(data->chat, "AT+CUSD=2", none_prefix,
cusd_cancel_cb, cbd, g_free) > 0)
return;
g_free(cbd);
CALLBACK_WITH_FAILURE(cb, user_data);
}
static void cusd_notify(GAtResult *result, gpointer user_data)
{
struct ofono_ussd *ussd = user_data;
cusd_parse(result, ussd);
}
static void at_ussd_register(gboolean ok, GAtResult *result, gpointer user)
{
struct ofono_ussd *ussd = user;
struct ussd_data *data = ofono_ussd_get_data(ussd);
if (!ok) {
ofono_error("Could not enable CUSD notifications");
ofono_ussd_remove(ussd);
return;
}
g_at_chat_register(data->chat, "+CUSD:", cusd_notify,
FALSE, ussd, NULL);
ofono_ussd_register(ussd);
}
static int at_ussd_probe(struct ofono_ussd *ussd, unsigned int vendor,
void *user)
{
GAtChat *chat = user;
struct ussd_data *data;
data = g_new0(struct ussd_data, 1);
data->chat = g_at_chat_clone(chat);
data->vendor = vendor;
ofono_ussd_set_data(ussd, data);
if(vendor == OFONO_VENDOR_TELIT)
{
g_at_chat_send(data->chat, "AT+CSCS=GSM", cscs_prefix,
charset_cb, data, NULL);
}
else
{
g_at_chat_send(data->chat, "AT+CSCS?", cscs_prefix,
read_charset_cb, data, NULL);
}
g_at_chat_send(data->chat, "AT+CUSD=1", NULL,
at_ussd_register, ussd, NULL);
return 0;
}
static void at_ussd_remove(struct ofono_ussd *ussd)
{
struct ussd_data *data = ofono_ussd_get_data(ussd);
ofono_ussd_set_data(ussd, NULL);
g_at_chat_unref(data->chat);
g_free(data);
}
static struct ofono_ussd_driver driver = {
.name = "atmodem",
.probe = at_ussd_probe,
.remove = at_ussd_remove,
.request = at_ussd_request,
.cancel = at_ussd_cancel
};
void at_ussd_init(void)
{
ofono_ussd_driver_register(&driver);
}
void at_ussd_exit(void)
{
ofono_ussd_driver_unregister(&driver);
}
^ permalink raw reply related [flat|nested] 5+ messages in thread