summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAki Niemi <aki.niemi@nokia.com>2010-11-14 18:32:53 +0200
committerAki Niemi <aki.niemi@nokia.com>2010-12-22 17:13:46 +0200
commitd1a0775a484f3e261b574fb9fc3702238840430c (patch)
tree8f9d598606e97fa07f68cc21403141f4194485e0
parent698e97c75ccdea1d90b3fe0bed728c3efb36e924 (diff)
downloadofono-d1a0775a484f3e261b574fb9fc3702238840430c.tar.bz2
isimodem: Adapt and refactor call forwarding
-rw-r--r--drivers/isimodem/call-forwarding.c523
1 files changed, 204 insertions, 319 deletions
diff --git a/drivers/isimodem/call-forwarding.c b/drivers/isimodem/call-forwarding.c
index f9ad7d39..9e61cfd3 100644
--- a/drivers/isimodem/call-forwarding.c
+++ b/drivers/isimodem/call-forwarding.c
@@ -32,6 +32,7 @@
#include <glib.h>
#include <gisi/client.h>
+#include <gisi/message.h>
#include <gisi/iter.h>
#include <ofono/log.h>
@@ -47,9 +48,22 @@ struct forw_data {
GIsiClient *client;
};
+struct forw_info {
+ uint8_t bsc; /* Basic service code */
+ uint8_t status; /* SS status */
+ uint8_t ton; /* Type of number */
+ uint8_t noreply; /* No reply timeout */
+ uint8_t forw_opt; /* Forwarding option */
+ uint8_t numlen; /* Number length */
+ uint8_t sublen; /* Sub-address length */
+ uint8_t filler;
+};
+
+
static int forw_type_to_isi_code(int type)
{
int ss_code;
+
switch (type) {
case 0:
ss_code = SS_GSM_FORW_UNCONDITIONAL;
@@ -77,290 +91,217 @@ static int forw_type_to_isi_code(int type)
return ss_code;
}
-static gboolean decode_gsm_forwarding_info(const void *restrict data,
- size_t len,
- uint8_t *status, uint8_t *ton,
- uint8_t *norply, char **number)
+static gboolean check_response_status(const GIsiMessage *msg, uint8_t msgid,
+ uint8_t service)
{
- GIsiSubBlockIter iter;
+ uint8_t type;
+
+ if (g_isi_msg_error(msg) < 0) {
+ DBG("Error: %s", strerror(-g_isi_msg_error(msg)));
+ return FALSE;
+ }
+
+ if (g_isi_msg_id(msg) != msgid) {
+ DBG("Unexpected msg: %s",
+ ss_message_id_name(g_isi_msg_id(msg)));
+ return FALSE;
+ }
- for (g_isi_sb_iter_init(&iter, data, len, 0);
- g_isi_sb_iter_is_valid(&iter);
- g_isi_sb_iter_next(&iter)) {
-
- switch (g_isi_sb_iter_get_id(&iter)) {
-
- case SS_GSM_FORWARDING_FEATURE: {
-
- uint8_t _numlen;
- uint8_t _status;
- uint8_t _norply;
- uint8_t _ton;
- char *_number = NULL;
-
- if (!g_isi_sb_iter_get_byte(&iter, &_status, 3)
- || !g_isi_sb_iter_get_byte(&iter, &_ton, 4)
- || !g_isi_sb_iter_get_byte(&iter, &_norply, 5)
- || !g_isi_sb_iter_get_byte(&iter, &_numlen, 7)
- || !g_isi_sb_iter_get_alpha_tag(&iter, &_number,
- _numlen * 2, 10))
- return FALSE;
-
- if (status)
- *status = _status;
- if (ton)
- *ton = _ton;
- if (norply)
- *norply = _norply;
- if (number)
- *number = _number;
- else
- g_free(_number);
-
- return TRUE;
- }
- default:
- DBG("Skipping sub-block: %s (%zd bytes)",
- ss_subblock_name(g_isi_sb_iter_get_id(&iter)),
- g_isi_sb_iter_get_len(&iter));
- break;
- }
+ if (!g_isi_msg_data_get_byte(msg, 0, &type)) {
+ DBG("Truncated message");
+ return FALSE;
+ }
+
+ if (type != service) {
+ DBG("Unexpected service type: 0x%02X", type);
+ return FALSE;
}
- return FALSE;
+ return TRUE;
}
-static gboolean registration_resp_cb(GIsiClient *client,
- const void *restrict data, size_t len,
- uint16_t object, void *opaque)
+static gboolean decode_gsm_forwarding_info(GIsiSubBlockIter *iter,
+ uint8_t *status, uint8_t *ton,
+ uint8_t *noreply, char **number)
{
- GIsiSubBlockIter iter;
- const unsigned char *msg = data;
- struct isi_cb_data *cbd = opaque;
- ofono_call_forwarding_set_cb_t cb = cbd->cb;
+ struct forw_info *info;
+ size_t len = sizeof(struct forw_info);
+ char *tag = NULL;
- if (!msg) {
- DBG("ISI client error: %d", g_isi_client_error(client));
- goto error;
- }
+ if (!g_isi_sb_iter_get_struct(iter, (void *)&info, len, 2))
+ return FALSE;
- if (len < 7 || msg[0] != SS_SERVICE_COMPLETED_RESP)
+ if (!g_isi_sb_iter_get_alpha_tag(iter, &tag, info->numlen * 2, len))
return FALSE;
- if (msg[1] != SS_REGISTRATION)
- goto error;
+ if (status)
+ *status = info->status;
- for (g_isi_sb_iter_init(&iter, msg, len, 7);
- g_isi_sb_iter_is_valid(&iter);
- g_isi_sb_iter_next(&iter)) {
+ if (ton)
+ *ton = info->ton;
- switch (g_isi_sb_iter_get_id(&iter)) {
+ if (noreply)
+ *noreply = info->noreply;
- case SS_GSM_ADDITIONAL_INFO:
- break;
+ if (number)
+ *number = tag;
+ else
+ g_free(tag);
- case SS_GSM_FORWARDING_INFO: {
+ return TRUE;
+}
- guint8 status;
- void *info = NULL;
- size_t infolen;
+static void registration_resp_cb(const GIsiMessage *msg, void *data)
+{
+ struct isi_cb_data *cbd = data;
+ ofono_call_forwarding_set_cb_t cb = cbd->cb;
+ GIsiSubBlockIter iter;
+ uint8_t status;
- if (!g_isi_sb_iter_get_data(&iter, &info, 4))
- goto error;
+ if (!check_response_status(msg, SS_SERVICE_COMPLETED_RESP,
+ SS_REGISTRATION))
+ goto error;
- infolen = g_isi_sb_iter_get_len(&iter) - 4;
+ for (g_isi_sb_iter_init(&iter, msg, 2);
+ g_isi_sb_iter_is_valid(&iter);
+ g_isi_sb_iter_next(&iter)) {
- if (!decode_gsm_forwarding_info(info, infolen, &status,
- NULL, NULL, NULL))
- goto error;
- if (!(status & SS_GSM_ACTIVE)
- || !(status & SS_GSM_REGISTERED))
- goto error;
+ if (g_isi_sb_iter_get_id(&iter) != SS_GSM_FORWARDING_INFO)
+ continue;
- break;
- }
- default:
- DBG("Skipping sub-block: %s (%zd bytes)",
- ss_subblock_name(g_isi_sb_iter_get_id(&iter)),
- g_isi_sb_iter_get_len(&iter));
- break;
- }
- }
+ if (!decode_gsm_forwarding_info(&iter, &status, NULL,
+ NULL, NULL))
+ goto error;
- CALLBACK_WITH_SUCCESS(cb, cbd->data);
- goto out;
+ if ((status & SS_GSM_ACTIVE) == 0 ||
+ (status & SS_GSM_REGISTERED) == 0)
+ goto error;
+
+ CALLBACK_WITH_SUCCESS(cb, cbd->data);
+ return;
+ }
error:
CALLBACK_WITH_FAILURE(cb, cbd->data);
-
-out:
- g_free(cbd);
- return TRUE;
}
-static void isi_registration(struct ofono_call_forwarding *cf,
- int type, int cls,
+static void isi_registration(struct ofono_call_forwarding *cf, int type,
+ int cls,
const struct ofono_phone_number *number,
- int time,
- ofono_call_forwarding_set_cb_t cb, void *data)
+ int time, ofono_call_forwarding_set_cb_t cb,
+ void *data)
{
struct forw_data *fd = ofono_call_forwarding_get_data(cf);
struct isi_cb_data *cbd = isi_cb_data_new(cf, cb, data);
- int ss_code;
- int num_filler;
- char *ucs2 = NULL;
- unsigned char msg[100] = {
+ int ss_code = forw_type_to_isi_code(type);
+ size_t numlen = strlen(number->number);
+ size_t sb_len = (numlen * 2 + 6 + 0) & ~3;
+
+ uint8_t msg[] = {
SS_SERVICE_REQ,
SS_REGISTRATION,
SS_GSM_TELEPHONY,
- 0, 0, /* Supplementary services code */
+ ss_code >> 8, /* Supplementary services code */
+ ss_code & 0xFF,
SS_SEND_ADDITIONAL_INFO,
1, /* Subblock count */
SS_FORWARDING,
- 0, /* Variable subblock length, because of phone number */
+ sb_len,
number->type,
time,
- strlen(number->number),
- 0 /* Sub address length */
+ numlen,
+ 0, /* Sub address length */
+ /*
+ * Followed by number in UCS-2, zero sub address
+ * bytes, and 0 to 3 bytes of filler
+ */
};
- /* Followed by number in UCS-2, zero sub address bytes, and 0
- * to 3 bytes of filler */
+ char *ucs2 = NULL;
- DBG("forwarding type %d class %d", type, cls);
+ const struct iovec iov[2] = {
+ { msg, sizeof(msg) },
+ { ucs2, numlen },
+ };
- if (cbd == NULL || fd == NULL || strlen(number->number) > 28)
- goto error;
+ DBG("forwarding type %d class %d\n", type, cls);
- ss_code = forw_type_to_isi_code(type);
- if (ss_code < 0)
+ if (cbd == NULL || fd == NULL || numlen > 28 || ss_code < 0)
goto error;
- msg[3] = ss_code >> 8;
- msg[4] = ss_code & 0xFF;
-
- num_filler = (6 + 2 * strlen(number->number)) % 4;
- if (num_filler != 0)
- num_filler = 4 - num_filler;
-
- msg[8] = 6 + 2 * strlen(number->number) + num_filler;
-
ucs2 = g_convert(number->number, strlen(number->number), "UCS-2BE",
"UTF-8//TRANSLIT", NULL, NULL, NULL);
if (ucs2 == NULL)
goto error;
- memcpy((char *)msg + 13, ucs2, strlen(number->number) * 2);
- g_free(ucs2);
-
- if (g_isi_request_make(fd->client, msg, 7 + msg[8], SS_TIMEOUT,
- registration_resp_cb, cbd))
- return;
+ if (g_isi_client_vsend(fd->client, iov, 2, SS_TIMEOUT,
+ registration_resp_cb, cbd, g_free))
+ goto out;
error:
CALLBACK_WITH_FAILURE(cb, data);
g_free(cbd);
+
+out:
+ g_free(ucs2);
}
-static gboolean erasure_resp_cb(GIsiClient *client,
- const void *restrict data, size_t len,
- uint16_t object, void *opaque)
+static void erasure_resp_cb(const GIsiMessage *msg, void *data)
{
- GIsiSubBlockIter iter;
- const unsigned char *msg = data;
- struct isi_cb_data *cbd = opaque;
+ struct isi_cb_data *cbd = data;
ofono_call_forwarding_set_cb_t cb = cbd->cb;
+ GIsiSubBlockIter iter;
+ uint8_t status;
- if (!msg) {
- DBG("ISI client error: %d", g_isi_client_error(client));
- goto error;
- }
-
- if (len < 7 || msg[0] != SS_SERVICE_COMPLETED_RESP)
- goto error;
-
- if (msg[1] != SS_ERASURE)
+ if (!check_response_status(msg, SS_SERVICE_COMPLETED_RESP, SS_ERASURE))
goto error;
- for (g_isi_sb_iter_init(&iter, msg, len, 7);
- g_isi_sb_iter_is_valid(&iter);
- g_isi_sb_iter_next(&iter)) {
-
- switch (g_isi_sb_iter_get_id(&iter)) {
-
- case SS_GSM_ADDITIONAL_INFO:
- break;
+ for (g_isi_sb_iter_init(&iter, msg, 6);
+ g_isi_sb_iter_is_valid(&iter);
+ g_isi_sb_iter_next(&iter)) {
- case SS_GSM_FORWARDING_INFO: {
+ if (g_isi_sb_iter_get_id(&iter) != SS_GSM_FORWARDING_INFO)
+ continue;
- guint8 status;
- void *info = NULL;
- size_t infolen;
+ if (!decode_gsm_forwarding_info(&iter, &status, NULL, NULL,
+ NULL))
+ goto error;
- if (!g_isi_sb_iter_get_data(&iter, &info, 4))
- goto error;
+ if (status & (SS_GSM_ACTIVE | SS_GSM_REGISTERED))
+ goto error;
- infolen = g_isi_sb_iter_get_len(&iter) - 4;
-
- if (!decode_gsm_forwarding_info(info, infolen, &status,
- NULL, NULL, NULL))
- goto error;
-
- if (status & (SS_GSM_ACTIVE | SS_GSM_REGISTERED))
- goto error;
-
- break;
- }
- default:
- DBG("Skipping sub-block: %s (%zd bytes)",
- ss_subblock_name(g_isi_sb_iter_get_id(&iter)),
- g_isi_sb_iter_get_len(&iter));
- break;
- }
+ CALLBACK_WITH_SUCCESS(cb, cbd->data);
+ return;
}
- CALLBACK_WITH_SUCCESS(cb, cbd->data);
- goto out;
-
error:
CALLBACK_WITH_FAILURE(cb, cbd->data);
-
-out:
- g_free(cbd);
- return TRUE;
}
-
static void isi_erasure(struct ofono_call_forwarding *cf, int type, int cls,
ofono_call_forwarding_set_cb_t cb, void *data)
{
struct forw_data *fd = ofono_call_forwarding_get_data(cf);
struct isi_cb_data *cbd = isi_cb_data_new(cf, cb, data);
- int ss_code;
+ int ss_code = forw_type_to_isi_code(type);
- unsigned char msg[] = {
+ const uint8_t msg[] = {
SS_SERVICE_REQ,
SS_ERASURE,
SS_GSM_TELEPHONY,
- 0, 0, /* Supplementary services code */
+ ss_code >> 8, /* Supplementary services code */
+ ss_code & 0xFF,
SS_SEND_ADDITIONAL_INFO,
- 0 /* Subblock count */
+ 0 /* Subblock count */
};
DBG("forwarding type %d class %d", type, cls);
- if (cbd == NULL || fd == NULL)
+ if (cbd == NULL || fd == NULL || ss_code < 0)
goto error;
- ss_code = forw_type_to_isi_code(type);
- if (ss_code < 0)
- goto error;
-
- msg[3] = ss_code >> 8;
- msg[4] = ss_code & 0xFF;
-
- if (g_isi_request_make(fd->client, msg, sizeof(msg), SS_TIMEOUT,
- erasure_resp_cb, cbd))
+ if (g_isi_client_send(fd->client, msg, sizeof(msg), SS_TIMEOUT,
+ erasure_resp_cb, cbd, g_free))
return;
error:
@@ -368,97 +309,63 @@ error:
g_free(cbd);
}
-static gboolean query_resp_cb(GIsiClient *client,
- const void *restrict data, size_t len,
- uint16_t object, void *opaque)
+static void query_resp_cb(const GIsiMessage *msg, void *data)
{
- GIsiSubBlockIter iter;
- const unsigned char *msg = data;
- struct isi_cb_data *cbd = opaque;
- ofono_call_forwarding_query_cb_t cb = cbd->cb;
-
- struct ofono_call_forwarding_condition list;
- list.status = 0;
- list.cls = 7;
- list.time = 0;
- list.phone_number.number[0] = 0;
- list.phone_number.type = 0;
- if (!msg) {
- DBG("ISI client error: %d", g_isi_client_error(client));
- goto error;
- }
+ struct isi_cb_data *cbd = data;
+ ofono_call_forwarding_query_cb_t cb = cbd->cb;
+ GIsiSubBlockIter iter;
- if (len < 7 || msg[0] != SS_SERVICE_COMPLETED_RESP)
- goto error;
+ struct ofono_call_forwarding_condition list = {
+ .status = 0,
+ .cls = 7,
+ .time = 0,
+ .phone_number = {
+ .number[0] = '\0',
+ .type = 0,
+ },
+ };
+ uint8_t status;
+ uint8_t ton;
+ uint8_t noreply;
+ char *number = NULL;
- if (msg[1] != SS_INTERROGATION)
+ if (!check_response_status(msg, SS_SERVICE_COMPLETED_RESP,
+ SS_INTERROGATION))
goto error;
- for (g_isi_sb_iter_init(&iter, msg, len, 7);
- g_isi_sb_iter_is_valid(&iter);
- g_isi_sb_iter_next(&iter)) {
-
- switch (g_isi_sb_iter_get_id(&iter)) {
-
- case SS_STATUS_RESULT:
- break;
-
- case SS_GSM_ADDITIONAL_INFO:
- break;
+ for (g_isi_sb_iter_init(&iter, msg, 6);
+ g_isi_sb_iter_is_valid(&iter);
+ g_isi_sb_iter_next(&iter)) {
- case SS_GSM_FORWARDING_INFO: {
+ if (g_isi_sb_iter_get_id(&iter) != SS_GSM_FORWARDING_INFO)
+ continue;
- guint8 status;
- void *info = NULL;
- size_t infolen;
+ if (!decode_gsm_forwarding_info(&iter, &status, &ton, &noreply,
+ &number))
+ goto error;
- guint8 ton;
- guint8 norply;
- char *number = NULL;
+ /* As in 27.007 section 7.11 */
+ list.status = status & SS_GSM_ACTIVE;
+ list.time = noreply;
+ list.phone_number.type = ton | 0x80;
- if (!g_isi_sb_iter_get_data(&iter, &info, 4))
- goto error;
+ strncpy(list.phone_number.number, number,
+ OFONO_MAX_PHONE_NUMBER_LENGTH);
+ list.phone_number.number[OFONO_MAX_PHONE_NUMBER_LENGTH] = '\0';
+ g_free(number);
- infolen = g_isi_sb_iter_get_len(&iter) - 4;
-
- if (!decode_gsm_forwarding_info(info, infolen, &status,
- &ton, &norply, &number))
- goto error;
-
- /* As in 27.007 section 7.11 */
- list.status = status & SS_GSM_ACTIVE;
- list.time = norply;
- list.phone_number.type = ton | 128;
- strncpy(list.phone_number.number, number,
- OFONO_MAX_PHONE_NUMBER_LENGTH);
- list.phone_number.number[OFONO_MAX_PHONE_NUMBER_LENGTH] = '\0';
- g_free(number);
-
- break;
- }
- default:
- DBG("Skipping sub-block: %s (%zd bytes)",
- ss_subblock_name(g_isi_sb_iter_get_id(&iter)),
- g_isi_sb_iter_get_len(&iter));
- break;
- }
- }
-
- DBG("forwarding query: %d, %d, %s(%d) - %d sec",
+ DBG("forwarding query: %d, %d, %s(%d) - %d sec",
list.status, list.cls,
list.phone_number.number,
list.phone_number.type, list.time);
- CALLBACK_WITH_SUCCESS(cb, 1, &list, cbd->data);
- goto out;
+
+ CALLBACK_WITH_SUCCESS(cb, 1, &list, cbd->data);
+ return;
+ }
error:
CALLBACK_WITH_FAILURE(cb, 0, NULL, cbd->data);
-
-out:
- g_free(cbd);
- return TRUE;
-
}
@@ -468,31 +375,25 @@ static void isi_query(struct ofono_call_forwarding *cf, int type, int cls,
{
struct forw_data *fd = ofono_call_forwarding_get_data(cf);
struct isi_cb_data *cbd = isi_cb_data_new(cf, cb, data);
- int ss_code;
+ int ss_code = forw_type_to_isi_code(type);
- unsigned char msg[] = {
+ const uint8_t msg[] = {
SS_SERVICE_REQ,
SS_INTERROGATION,
SS_GSM_TELEPHONY,
- 0, 0, /* Supplementary services code */
+ ss_code >> 8, /* Supplementary services code */
+ ss_code & 0xFF,
SS_SEND_ADDITIONAL_INFO,
- 0 /* Subblock count */
+ 0 /* Subblock count */
};
DBG("forwarding type %d class %d", type, cls);
- if (cbd == NULL || fd == NULL || cls != 7)
- goto error;
-
- ss_code = forw_type_to_isi_code(type);
- if (ss_code < 0)
+ if (cbd == NULL || fd == NULL || cls != 7 || ss_code < 0)
goto error;
- msg[3] = ss_code >> 8;
- msg[4] = ss_code & 0xFF;
-
- if (g_isi_request_make(fd->client, msg, sizeof(msg), SS_TIMEOUT,
- query_resp_cb, cbd))
+ if (g_isi_client_send(fd->client, msg, sizeof(msg), SS_TIMEOUT,
+ query_resp_cb, cbd, g_free))
return;
error:
@@ -500,57 +401,40 @@ error:
g_free(cbd);
}
-static gboolean isi_call_forwarding_register(gpointer user)
-{
- struct ofono_call_forwarding *cf = user;
-
- ofono_call_forwarding_register(cf);
-
- return FALSE;
-}
-
-static void reachable_cb(GIsiClient *client, gboolean alive, uint16_t object,
- void *opaque)
+static void reachable_cb(const GIsiMessage *msg, void *data)
{
- struct ofono_call_forwarding *cf = opaque;
- const char *debug = NULL;
+ struct ofono_call_forwarding *cf = data;
- if (!alive) {
- DBG("Unable to bootstrap call forwarding driver");
+ if (g_isi_msg_error(msg) < 0) {
+ DBG("No QSO!");
return;
}
- DBG("%s (v%03d.%03d) reachable",
- pn_resource_name(g_isi_client_resource(client)),
- g_isi_version_major(client),
- g_isi_version_minor(client));
+ ISI_VERSION_DBG(msg);
- debug = getenv("OFONO_ISI_DEBUG");
- if (debug && (strcmp(debug, "all") == 0 || strcmp(debug, "ss") == 0))
- g_isi_client_set_debug(client, ss_debug, NULL);
-
- g_idle_add(isi_call_forwarding_register, cf);
+ ofono_call_forwarding_register(cf);
}
static int isi_call_forwarding_probe(struct ofono_call_forwarding *cf,
unsigned int vendor, void *user)
{
- GIsiModem *idx = user;
- struct forw_data *data;
+ GIsiModem *modem = user;
+ struct forw_data *fd;
- data = g_try_new0(struct forw_data, 1);
- if (data == NULL)
+ fd = g_try_new0(struct forw_data, 1);
+ if (fd == NULL)
return -ENOMEM;
- data->client = g_isi_client_create(idx, PN_SS);
- if (data->client == NULL)
+ fd->client = g_isi_client_create(modem, PN_SS);
+ if (fd->client == NULL) {
+ g_free(fd);
return -ENOMEM;
+ }
- ofono_call_forwarding_set_data(cf, data);
+ ofono_call_forwarding_set_data(cf, fd);
- if (!g_isi_verify(data->client, reachable_cb, cf))
- DBG("Unable to verify reachability");
+ g_isi_client_verify(fd->client, reachable_cb, cf, NULL);
return 0;
}
@@ -559,10 +443,11 @@ static void isi_call_forwarding_remove(struct ofono_call_forwarding *cf)
{
struct forw_data *data = ofono_call_forwarding_get_data(cf);
+ ofono_call_forwarding_set_data(cf, NULL);
+
if (data == NULL)
return;
- ofono_call_forwarding_set_data(cf, NULL);
g_isi_client_destroy(data->client);
g_free(data);
}