summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexander Kanavin <alexander.kanavin@nokia.com>2009-08-13 19:54:26 +0300
committerAki Niemi <aki.niemi@nokia.com>2009-08-14 16:55:55 +0300
commit2076a044ecc2e9af5e188ad2de312520d6540739 (patch)
tree146f279aa72a8710551a187c9d8aa02c28a4ed38
parent01b4c494477150a2630c5bf734119e2f8fa53c67 (diff)
downloadofono-2076a044ecc2e9af5e188ad2de312520d6540739.tar.bz2
Add ISI modem SIM phonebook support
This patch only provides access to the SIM phonebook. Note that the interface to the SIM phonebook is quite slow, which may cause timeouts on phonebooks with a large number of entries using the default D-Bus timeout value.
-rw-r--r--drivers/Makefile.am2
-rw-r--r--drivers/isimodem/isi.h10
-rw-r--r--drivers/isimodem/isimodem.c2
-rw-r--r--drivers/isimodem/isiphonebook.c425
4 files changed, 438 insertions, 1 deletions
diff --git a/drivers/Makefile.am b/drivers/Makefile.am
index 99582311..b4d44269 100644
--- a/drivers/Makefile.am
+++ b/drivers/Makefile.am
@@ -13,7 +13,7 @@ builtin_sources += atmodem/atmodem.c atmodem/at.h \
atmodem/call-barring.c atmodem/phonebook.c
builtin_modules += isimodem
-builtin_sources += isimodem/isimodem.c isimodem/isi.h
+builtin_sources += isimodem/isimodem.c isimodem/isi.h isimodem/isiphonebook.c
noinst_LTLIBRARIES = libbuiltin.la
diff --git a/drivers/isimodem/isi.h b/drivers/isimodem/isi.h
index e808bfd8..f753d386 100644
--- a/drivers/isimodem/isi.h
+++ b/drivers/isimodem/isi.h
@@ -26,9 +26,16 @@
e.type = OFONO_ERROR_TYPE_FAILURE; \
e.error = 0 \
+#define DECLARE_SUCCESS(e) \
+ struct ofono_error e; \
+ e.type = OFONO_ERROR_TYPE_NO_ERROR; \
+ e.error = 0 \
+
+
struct isi_data {
struct ofono_modem *modem;
struct netreg_data *netreg;
+ struct pb_data *pb;
};
struct isi_cb_data {
@@ -54,3 +61,6 @@ static inline struct isi_cb_data *isi_cb_data_new(struct ofono_modem *modem,
}
void dump_msg(const unsigned char *msg, size_t len);
+
+extern void isi_phonebook_init(struct ofono_modem *modem);
+extern void isi_phonebook_exit(struct ofono_modem *modem);
diff --git a/drivers/isimodem/isimodem.c b/drivers/isimodem/isimodem.c
index d4eaa4e1..f611eb51 100644
--- a/drivers/isimodem/isimodem.c
+++ b/drivers/isimodem/isimodem.c
@@ -429,6 +429,7 @@ static void netlink_status_cb(bool up, uint8_t addr, unsigned idx,
return;
ofono_modem_set_userdata(isi->modem, isi);
+ isi_phonebook_init(isi->modem);
}
} else {
clear_pending_reqs();
@@ -439,6 +440,7 @@ static void netlink_status_cb(bool up, uint8_t addr, unsigned idx,
}
if (isi->modem) {
+ isi_phonebook_exit(isi->modem);
ofono_modem_unregister(isi->modem);
isi->modem = NULL;
}
diff --git a/drivers/isimodem/isiphonebook.c b/drivers/isimodem/isiphonebook.c
new file mode 100644
index 00000000..6510e21f
--- /dev/null
+++ b/drivers/isimodem/isiphonebook.c
@@ -0,0 +1,425 @@
+/*
+ * This file is part of oFono - Open Source Telephony
+ *
+ * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
+ *
+ * Contact: Alexander Kanavin <alexander.kanavin@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glib.h>
+#include <gisi/client.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include "driver.h"
+#include "util.h"
+
+#include "isi.h"
+
+#define PHONEBOOK_TIMEOUT 5
+#define PN_SIM 0x09
+
+enum pb_message_id {
+ SIM_PB_REQ_SIM_PB_READ = 0xDC,
+ SIM_PB_RESP_SIM_PB_READ = 0xDD
+};
+
+enum pb_service_types {
+ SIM_PB_READ = 0x0F
+};
+
+enum pb_sub_block_id {
+ SIM_PB_INFO_REQUEST = 0xE4,
+ SIM_PB_STATUS = 0xFB,
+ SIM_PB_LOCATION = 0xFE,
+ SIM_PB_LOCATION_SEARCH = 0xFF
+};
+
+enum pb_type {
+ SIM_PB_ADN = 0xC8
+};
+
+enum pb_tag {
+ SIM_PB_ANR = 0xCA,
+ SIM_PB_EMAIL = 0xDD,
+ SIM_PB_SNE = 0xF7
+};
+
+enum pb_status {
+ SIM_SERV_OK = 0x01
+};
+
+static GIsiClient *client = NULL;
+
+struct pb_data {
+
+};
+
+static char *ucs2_to_utf8(const char *str, long len)
+{
+ char *utf8;
+ utf8 = g_convert(str, len, "UTF-8//TRANSLIT", "UCS-2BE",
+ NULL, NULL, NULL);
+ return utf8;
+}
+
+
+static struct pb_data *phonebook_create()
+{
+ struct pb_data *pb = g_try_new0(struct pb_data, 1);
+ return pb;
+}
+
+static void phonebook_destroy(struct pb_data *data)
+{
+ g_free(data);
+}
+
+static int decode_read_response(const unsigned char *msg, size_t len,
+ struct ofono_modem *modem)
+{
+ unsigned int i, p;
+
+ char *name = NULL;
+ char *number = NULL;
+ char *adn = NULL;
+ char *snr = NULL;
+ char *email = NULL;
+
+ unsigned int location = 0;
+ unsigned int status = 0;
+
+ unsigned int messageid;
+ unsigned int servicetype;
+ unsigned int num_subblocks;
+
+ dump_msg(msg, len);
+
+ if (len < 3)
+ goto error;
+
+ messageid = msg[0];
+ servicetype = msg[1];
+ num_subblocks = msg[2];
+
+ if (messageid != SIM_PB_RESP_SIM_PB_READ || servicetype != SIM_PB_READ)
+ goto error;
+
+ p = 3;
+ for (i=0; i < num_subblocks; i++) {
+ unsigned int subblock_type;
+ unsigned int subblock_len;
+
+ if (p + 4 > len)
+ goto error;
+
+ subblock_type = (msg[p] << 8) + msg[p+1];
+ subblock_len = (msg[p+2] << 8) + msg[p+3];
+
+ switch (subblock_type) {
+
+ case SIM_PB_ADN: {
+ unsigned int namelength;
+ unsigned int numberlength;
+
+ if (p + 8 > len)
+ goto error;
+
+ location = (msg[p + 4] << 8) + msg[p + 5];
+ namelength = msg[p + 6];
+ numberlength = msg[p + 7];
+
+ if (p + 8 + namelength * 2 + numberlength * 2 > len)
+ goto error;
+
+ name = ucs2_to_utf8(msg + p + 8, namelength * 2);
+ number = ucs2_to_utf8(msg + p + 8 + namelength * 2,
+ numberlength * 2);
+ DBG("ADN subblock: name %s number %s location %i",
+ name, number, location);
+ break;
+ }
+
+ case SIM_PB_SNE: {
+ unsigned int locsne;
+ unsigned int snelength;
+ unsigned int snefiller;
+
+ if (p + 8 > len)
+ goto error;
+
+ locsne = (msg[p + 4] << 8) + msg[p + 5];
+ snelength = msg[p + 6];
+ snefiller = msg[p + 7];
+
+ if (p + 8 + snelength * 2 > len)
+ goto error;
+
+ adn = ucs2_to_utf8(msg + p + 8, snelength * 2);
+ DBG("SNE subblock: name %s", adn);
+ break;
+ }
+
+ case SIM_PB_ANR: {
+ unsigned int locanr;
+ unsigned int anrlength;
+ unsigned int anrfiller;
+
+ if (p + 8 > len)
+ goto error;
+
+ locanr = (msg[p + 4] << 8) + msg[p + 5];
+ anrlength = msg[p + 6];
+ anrfiller = msg[p + 7];
+
+ if (p + 8 + anrlength * 2 > len)
+ goto error;
+
+ snr = ucs2_to_utf8(msg + p + 8, anrlength * 2);
+ DBG("ANR subblock: number %s", snr);
+ break;
+ }
+
+ case SIM_PB_EMAIL: {
+ unsigned int locemail;
+ unsigned int emaillength;
+ unsigned int emailfiller;
+
+ if (p + 8 > len)
+ goto error;
+
+ locemail = (msg[p + 4] << 8) + msg[p + 5];
+ emaillength = msg[p + 6];
+ emailfiller = msg[p + 7];
+
+ if (p + 8 + emaillength * 2 > len)
+ goto error;
+
+ email = ucs2_to_utf8(msg + p + 8, emaillength * 2);
+ DBG("EMAIL subblock: email %s", email);
+ break;
+ }
+
+ case SIM_PB_STATUS:
+ if (p + 5 > len)
+ goto error;
+
+ status = msg[p + 4];
+ DBG("STATUS subblock: status %i", status);
+ break;
+
+ default:
+ DBG("Unknown subblock in read response: type %i length %i",
+ subblock_type, subblock_len);
+ break;
+ }
+
+ p += subblock_len;
+ }
+ if (status == SIM_SERV_OK) {
+ ofono_phonebook_entry(modem, -1, number, -1, name, -1, NULL,
+ snr, -1, adn, email, NULL, NULL);
+ return location;
+ } else {
+ return -1;
+ }
+
+error:
+ DBG("Malformed read response");
+ return -1;
+}
+
+static void read_next_entry(int location, GIsiResponseFunc read_cb, struct isi_cb_data *cbd)
+{
+ ofono_generic_cb_t cb = cbd->cb;
+ const unsigned char msg[] = {
+ SIM_PB_REQ_SIM_PB_READ,
+ SIM_PB_READ,
+ 2, /* number of subblocks */
+ 0, SIM_PB_LOCATION_SEARCH, /* subblock id */
+ 0, 8, /* subblock size */
+ 0, SIM_PB_ADN,
+ location >> 8, location & 0xFF, /* read next entry after specified by location */
+ 0, SIM_PB_INFO_REQUEST, /* subblock id */
+ 0, 16, /* subblock size */
+ 4, /* number of tags */
+ 0, /* filler */
+ 0, SIM_PB_ADN, /* tags */
+ 0, SIM_PB_SNE,
+ 0, SIM_PB_ANR,
+ 0, SIM_PB_EMAIL,
+ 0, 0 /* filler */
+ };
+
+ if (location < 0)
+ goto error;
+
+ if (!cbd)
+ goto error;
+
+ if (g_isi_request_make(client, msg, sizeof(msg), PHONEBOOK_TIMEOUT,
+ read_cb, cbd))
+ return;
+
+error:
+ {
+ DECLARE_FAILURE(error);
+ cb(&error, cbd->data);
+ g_free(cbd);
+ }
+
+}
+
+static bool read_resp_cb(GIsiClient *client, const void *restrict data,
+ size_t len, uint16_t object, void *opaque)
+{
+ const unsigned char *msg = data;
+ struct isi_cb_data *cbd = opaque;
+ ofono_generic_cb_t cb = cbd->cb;
+ int location;
+
+ if(!msg) {
+ DBG("ISI client error: %d", g_isi_client_error(client));
+ goto error;
+ }
+
+ location = decode_read_response(data, len, cbd->modem);
+ if (location != -1) {
+ read_next_entry(location, read_resp_cb, cbd);
+ return;
+ }
+
+ {
+ DECLARE_SUCCESS(e);
+ cb(&e, cbd->data);
+ }
+
+ goto out;
+
+error:
+ {
+ DECLARE_FAILURE(e);
+ cb(&e, cbd->data);
+ }
+
+out:
+ g_free(cbd);
+ return true;
+}
+
+static void isi_export_entries(struct ofono_modem *modem, const char *storage,
+ ofono_generic_cb_t cb, void *data)
+{
+ struct isi_cb_data *cbd = isi_cb_data_new(modem, cb, data);
+ const unsigned char msg[] = {
+ SIM_PB_REQ_SIM_PB_READ,
+ SIM_PB_READ,
+ 2, /* number of subblocks */
+ 0, SIM_PB_LOCATION, /* subblock id */
+ 0, 8, /* subblock size */
+ 0, SIM_PB_ADN,
+ 0xFF, 0xFF, /* read first entry in the phonebook */
+ 0, SIM_PB_INFO_REQUEST, /* subblock id */
+ 0, 16, /* subblock size */
+ 4, /* number of tags */
+ 0, /* filler */
+ 0, SIM_PB_ADN, /* tags */
+ 0, SIM_PB_SNE,
+ 0, SIM_PB_ANR,
+ 0, SIM_PB_EMAIL,
+ 0, 0 /* filler */
+ };
+
+ if (!cbd)
+ goto error;
+
+ if (strcmp(storage, "SM"))
+ goto error;
+
+ if (g_isi_request_make(client, msg, sizeof(msg), PHONEBOOK_TIMEOUT,
+ read_resp_cb, cbd))
+ return;
+
+error:
+ if (cbd)
+ g_free(cbd);
+
+ {
+ DECLARE_FAILURE(error);
+ cb(&error, data);
+ }
+}
+
+static struct ofono_phonebook_ops ops = {
+ .export_entries = isi_export_entries
+};
+
+static void phonebook_not_supported(struct ofono_modem *modem)
+{
+ struct isi_data *isi = ofono_modem_get_userdata(modem);
+
+ ofono_error("Phonebook not supported by this modem. If this is in "
+ "error please submit patches to support this hardware");
+ if (isi->pb) {
+ phonebook_destroy(isi->pb);
+ isi->pb = NULL;
+ }
+}
+
+void isi_phonebook_init(struct ofono_modem *modem)
+{
+ struct isi_data *isi = ofono_modem_get_userdata(modem);
+
+ isi->pb = phonebook_create();
+ if (!client) {
+ client = g_isi_client_create(PN_SIM);
+ if (!client)
+ goto error;
+ }
+
+ /* FIXME: If this is running on a phone itself, phonebook initialization needs to be done here */
+ ofono_phonebook_register(modem, &ops);
+ return;
+
+error:
+ phonebook_not_supported(modem);
+}
+
+void isi_phonebook_exit(struct ofono_modem *modem)
+{
+ struct isi_data *isi = ofono_modem_get_userdata(modem);
+
+ ofono_phonebook_unregister(modem);
+
+ if (client) {
+ g_isi_client_destroy(client);
+ client = NULL;
+ }
+ if (!isi->pb)
+ return;
+
+ phonebook_destroy(isi->pb);
+ isi->pb = NULL;
+}