From bca2cdffbcf1c05f6bf98eff8b55c889a7ba44f1 Mon Sep 17 00:00:00 2001 From: Aki Niemi Date: Fri, 14 May 2010 16:45:48 +0300 Subject: [PATCH] Add isimodem support for MO and MT SMS --- drivers/isimodem/sms.c | 308 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 293 insertions(+), 15 deletions(-) diff --git a/drivers/isimodem/sms.c b/drivers/isimodem/sms.c index f4da77f9..b49b1adf 100644 --- a/drivers/isimodem/sms.c +++ b/drivers/isimodem/sms.c @@ -1,9 +1,7 @@ /* * This file is part of oFono - Open Source Telephony * - * Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). - * - * Contact: Aki Niemi + * Copyright (C) 2009-2010 Nokia Corporation and/or its subsidiary(-ies). * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -30,15 +28,19 @@ #include #include #include +#include +#include #include #include +#include #include #include #include +#include "smsutil.h" #include "isimodem.h" #include "isiutil.h" #include "sms.h" @@ -48,8 +50,8 @@ struct sms_data { GIsiClient *client; }; -static void isi_sca_query(struct ofono_sms *sms, ofono_sms_sca_query_cb_t cb, - void *data) +static void isi_sca_query(struct ofono_sms *sms, + ofono_sms_sca_query_cb_t cb, void *data) { DBG("Not implemented."); CALLBACK_WITH_FAILURE(cb, NULL, data); @@ -63,18 +65,263 @@ static void isi_sca_set(struct ofono_sms *sms, CALLBACK_WITH_FAILURE(cb, data); } +static bool submit_resp_cb(GIsiClient *client, const void *restrict data, + size_t len, uint16_t object, void *opaque) +{ + const uint8_t *msg = data; + struct isi_cb_data *cbd = opaque; + ofono_sms_submit_cb_t cb = cbd->cb; + + int mr = -1; + GIsiSubBlockIter iter; + + if (!msg) { + DBG("ISI client error: %d", g_isi_client_error(client)); + goto error; + } + + if (len < 3 || msg[0] != SMS_MESSAGE_SEND_RESP) + return false; + + for (g_isi_sb_iter_init(&iter, msg, len, 3); + g_isi_sb_iter_is_valid(&iter); + g_isi_sb_iter_next(&iter)) { + + uint8_t type; + uint8_t cause; + uint8_t ref; + + switch (g_isi_sb_iter_get_id(&iter)) { + + case SMS_GSM_REPORT: + + if (!g_isi_sb_iter_get_byte(&iter, &type, 2) + || !g_isi_sb_iter_get_byte(&iter, &cause, 3) + || !g_isi_sb_iter_get_byte(&iter, &ref, 4)) + goto error; + + if (cause != 0) { + DBG("Submit error: 0x%"PRIx8" (type 0x%"PRIx8")", + cause, type); + goto error; + } + + DBG("cause=0x%"PRIx8", type 0x%"PRIx8", mr=0x%"PRIx8, + cause, type, ref); + + mr = (int)ref; + break; + + default: + DBG("skipped sub-block: %s (%zu bytes)", + sms_subblock_name(g_isi_sb_iter_get_id(&iter)), + g_isi_sb_iter_get_len(&iter)); + + } + } + + if (mr == -1) + goto error; + + CALLBACK_WITH_SUCCESS(cb, mr, cbd->data); + goto out; + +error: + CALLBACK_WITH_FAILURE(cb, -1, cbd->data); + +out: + g_free(cbd); + return true; +} + static void isi_submit(struct ofono_sms *sms, unsigned char *pdu, int pdu_len, int tpdu_len, int mms, ofono_sms_submit_cb_t cb, void *data) { - DBG("Not implemented."); + struct sms_data *sd = ofono_sms_get_data(sms); + struct isi_cb_data *cbd = isi_cb_data_new(sms, cb, data); + + uint8_t *sca = pdu; + uint8_t sca_len = pdu_len - tpdu_len; + uint8_t sca_sb_len = 4 + sca_len; + + uint8_t *tpdu = pdu + sca_len; + uint8_t ud_sb_len = 4 + tpdu_len; + + uint8_t use_default = sca_len == 1 && sca[0] == 0; + + uint8_t msg[] = { + SMS_MESSAGE_SEND_REQ, + mms, + SMS_ROUTE_CS_PREF, + 0, /* Is this a re-send? */ + SMS_SENDER_ANY, + SMS_TYPE_TEXT_MESSAGE, + 1, /* Sub blocks */ + SMS_GSM_TPDU, + 4 + ud_sb_len + (use_default ? 0 : sca_sb_len), + 0, /* Filler */ + 1 + (use_default ? 0 : 1), /* Sub blocks */ + SMS_COMMON_DATA, + ud_sb_len, + tpdu_len, + 0, /* Packing required? */ + /* TPDU */ + }; + + uint8_t scaddr[] = { + SMS_ADDRESS, + sca_sb_len, + SMS_GSM_0411_ADDRESS, + sca_len, + /* SCA */ + }; + + struct iovec iov[4] = { + { msg, sizeof(msg) }, + { tpdu, tpdu_len }, + { scaddr, sizeof(scaddr) }, + { sca, sca_len }, + }; + + if (!cbd) + goto error; + + if (g_isi_request_vmake(sd->client, iov, use_default ? 2 : 4, SMS_TIMEOUT, + submit_resp_cb, cbd)) + return; + +error: CALLBACK_WITH_FAILURE(cb, -1, data); + g_free(cbd); +} + +static void send_status_ind_cb(GIsiClient *client, const void *restrict data, + size_t len, uint16_t object, void *opaque) +{ + const uint8_t *msg = data; + //struct ofono_sms *sms = opaque; + + if (!msg || len < 6 || msg[0] != SMS_MESSAGE_SEND_STATUS_IND) + return; + + DBG("status=0x%"PRIx8", mr=0x%"PRIx8", route=0x%"PRIx8 + ", cseg=0x%"PRIx8", tseg=0x%"PRIx8, + msg[1], msg[2], msg[3], msg[4], msg[5]); + + DBG("TODO: Status notification"); +} + +static bool report_resp_cb(GIsiClient *client, const void *restrict data, + size_t len, uint16_t object, void *opaque) +{ + const uint8_t *msg = data; + + if (!msg) { + DBG("ISI client error: %d", g_isi_client_error(client)); + return true; + } + + if (len < 3 || msg[0] != SMS_GSM_RECEIVED_PP_REPORT_RESP) + return false; + + DBG("Report resp cause=0x%"PRIx8, msg[1]); + return true; +} + +static bool send_deliver_report(GIsiClient *client, bool success) +{ + uint8_t cause_type = !success ? SMS_CAUSE_TYPE_GSM : 0; + uint8_t cause = !success ? SMS_GSM_ERR_MEMORY_CAPACITY_EXC : 0; + + uint8_t msg[] = { + SMS_GSM_RECEIVED_PP_REPORT_REQ, + cause_type, /* Cause type */ + cause, /* SMS cause */ + 0, 0, 0, /* Filler */ + 1, /* Sub blocks */ + SMS_GSM_DELIVER_REPORT, + 8, + 0, /* Message parameters */ + 0, /* Cause type */ + 0, 0, 0, /* Filler */ + 0, /* Sub blocks */ + }; + + return g_isi_request_make(client, msg, sizeof(msg), SMS_TIMEOUT, + report_resp_cb, NULL); } static void routing_ntf_cb(GIsiClient *client, const void *restrict data, size_t len, uint16_t object, void *opaque) { - DBG("Not implemented."); + const uint8_t *msg = data; + struct ofono_sms *sms = opaque; + GIsiSubBlockIter iter; + + uint8_t *sca = NULL; + uint8_t sca_len = 0; + uint8_t *tpdu = NULL; + uint8_t tpdu_len = 0; + + unsigned char pdu[176]; + + if (!msg || len < 7 || msg[0] != SMS_PP_ROUTING_NTF + || msg[3] != SMS_GSM_TPDU) + return; + + 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)) { + + uint8_t type; + void *data; + uint8_t data_len; + + case SMS_ADDRESS: + + if (!g_isi_sb_iter_get_byte(&iter, &type, 2) + || !g_isi_sb_iter_get_byte(&iter, &data_len, 3) + || !g_isi_sb_iter_get_data(&iter, &data, 4) + || type != SMS_GSM_0411_ADDRESS) + break; + + sca = data; + sca_len = data_len; + break; + + case SMS_COMMON_DATA: + + if (!g_isi_sb_iter_get_byte(&iter, &data_len, 2) + || !g_isi_sb_iter_get_data(&iter, &data, 4)) + break; + + tpdu = data; + tpdu_len = data_len; + break; + + default: + DBG("skipped sub-block: %s (%zu bytes)", + sms_subblock_name(g_isi_sb_iter_get_id(&iter)), + g_isi_sb_iter_get_len(&iter)); + } + } + + if (!tpdu || !sca || tpdu_len + sca_len > sizeof(pdu)) + return; + + memcpy(pdu, sca, sca_len); + memcpy(pdu + sca_len, tpdu, tpdu_len); + + ofono_sms_deliver_notify(sms, pdu, tpdu_len + sca_len, tpdu_len); + + /* FIXME: We should not ack the DELIVER unless it has been + * reliably stored, i.e., written to disk. Currently, there is + * no such indication from core, so we just blindly trust that + * it did The Right Thing here. */ + send_deliver_report(client, true); } static bool routing_resp_cb(GIsiClient *client, const void *restrict data, @@ -83,8 +330,6 @@ static bool routing_resp_cb(GIsiClient *client, const void *restrict data, const unsigned char *msg = data; struct ofono_sms *sms = opaque; - DBG(""); - if (!msg) { DBG("ISI client error: %d", g_isi_client_error(client)); goto error; @@ -94,10 +339,23 @@ static bool routing_resp_cb(GIsiClient *client, const void *restrict data, goto error; if (msg[1] != SMS_OK) { - DBG("Request failed: 0x%02X", msg[1]); - goto error; + + if (msg[1] == SMS_ERR_PP_RESERVED) { + DBG("Request failed: 0x%02"PRIx8" (%s).\n\n " + "Unable to bootstrap SMS routing.\n " + "It appears some other component is " + "already\n registered as the SMS " + "routing endpoint.\n As a consequence, " + "receiving SMSs is NOT going to work.\n " + "Receiving on the other hand might work.\n\n", + msg[1], sms_isi_cause_name(msg[1])); + ofono_sms_register(sms); + } + return true; } + g_isi_subscribe(client, SMS_PP_ROUTING_NTF, routing_ntf_cb, sms); + ofono_sms_register(sms); return true; @@ -134,8 +392,8 @@ static int isi_sms_probe(struct ofono_sms *sms, unsigned int vendor, ofono_sms_set_data(sms, data); g_isi_client_set_debug(data->client, sms_debug, NULL); - g_isi_subscribe(data->client, SMS_PP_ROUTING_NTF, routing_ntf_cb, sms); - + g_isi_subscribe(data->client, SMS_MESSAGE_SEND_STATUS_IND, + send_status_ind_cb, sms); if (!g_isi_request_make(data->client, msg, sizeof(msg), SMS_TIMEOUT, routing_resp_cb, sms)) DBG("Failed to set SMS routing."); @@ -147,10 +405,30 @@ static void isi_sms_remove(struct ofono_sms *sms) { struct sms_data *data = ofono_sms_get_data(sms); - if (data) { + const unsigned char msg[] = { + SMS_PP_ROUTING_REQ, + SMS_ROUTING_RELEASE, + 0x01, /* Sub-block count */ + SMS_GSM_ROUTING, + 0x08, /* Sub-block length */ + SMS_GSM_TPDU_ROUTING, + SMS_GSM_MT_ALL_TYPE, + 0x00, 0x00, 0x00, /* Filler */ + 0x00 /* Sub-sub-block count */ + }; + + if (!data) + return; + + if (data->client) { + /* Send a promiscuous routing release, so as not to + * hog resources unnecessarily after being removed */ + g_isi_request_make(data->client, msg, sizeof(msg), + SMS_TIMEOUT, NULL, NULL); g_isi_client_destroy(data->client); - g_free(data); } + + g_free(data); } static struct ofono_sms_driver driver = {