All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 2/2] Telit HE910 general fixes for gprs, sms and voicecall
@ 2015-03-06 16:07 Enrico Sau
  2015-03-09  9:02 ` Kallumari
                   ` (2 more replies)
  0 siblings, 3 replies; 5+ messages in thread
From: Enrico Sau @ 2015-03-06 16:07 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 3333 bytes --]

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: 10044 bytes --]

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* 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

* 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:54   ` Denis Kenzior
  2015-03-10  1:50 ` Denis Kenzior
  2 siblings, 1 reply; 5+ messages in thread
From: Kallumari @ 2015-03-09 12:21 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 3865 bytes --]

Guys,

I am able to connect to the cellular network now.
I killed a process pppd which was also using the ttyACM0 port.

However, I will have to try out more than 1 time the following process,
ofonod -> enable-modem -> activate-context

for every second time it works, no idea yet on the reason.

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: 19153 bytes --]

^ permalink raw reply related	[flat|nested] 5+ messages in thread

* 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: Denis Kenzior @ 2015-03-10  1:50 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 3811 bytes --]

Hi Enrico,

On 03/06/2015 10:07 AM, Enrico Sau wrote:
> 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);

I suspect this is not needed on newer firmware.

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

Same as above

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

The CSCS setting should be in plugins/he910.c.  That way you don't need 
to mess with vendor specific code here.

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

If these are not supported, then these commands will return an error. 
Should be safe to leave this in.

Regards,
-Denis

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 2/2] Telit HE910 general fixes for gprs, sms and voicecall
  2015-03-09 12:21 ` Kallumari
@ 2015-03-10  1:54   ` Denis Kenzior
  0 siblings, 0 replies; 5+ messages in thread
From: Denis Kenzior @ 2015-03-10  1:54 UTC (permalink / raw)
  To: ofono

[-- Attachment #1: Type: text/plain, Size: 890 bytes --]

Hi Ram,

Please don't top post on this mailing list.  Your answers should be inlined.

http://en.wikipedia.org/wiki/Posting_style#Top-posting

On 03/09/2015 07:21 AM, Kallumari Nagaraja Rao, RammohanX wrote:
> Guys,
>
> I am able to connect to the cellular network now.
>
> I killed a process pppd which was also using the ttyACM0 port.
>

Please make sure that no other modem management software is active. 
oFono requires exclusive access to all tty ports associated with the 
modem hardware.

So if you're running NetworkManager, ModemManager, pppd, etc, these must 
be stopped or configured to ignore the modem hardware you're trying to 
use with oFono.

> However, I will have to try out more than 1 time the following process,
>
> ofonod -> enable-modem -> activate-context
>
> for every second time it works, no idea yet on the reason.

Regards,
-Denis

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2015-03-10  1:54 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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:54   ` Denis Kenzior
2015-03-10  1:50 ` Denis Kenzior

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.