Kenji Arai / mbed-os_TYBLE16

Dependents:   TYBLE16_simple_data_logger TYBLE16_MP3_Air

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers AT_CellularSMS.cpp Source File

AT_CellularSMS.cpp

00001 /*
00002  * Copyright (c) 2017, Arm Limited and affiliates.
00003  * SPDX-License-Identifier: Apache-2.0
00004  *
00005  * Licensed under the Apache License, Version 2.0 (the "License");
00006  * you may not use this file except in compliance with the License.
00007  * You may obtain a copy of the License at
00008  *
00009  *     http://www.apache.org/licenses/LICENSE-2.0
00010  *
00011  * Unless required by applicable law or agreed to in writing, software
00012  * distributed under the License is distributed on an "AS IS" BASIS,
00013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00014  * See the License for the specific language governing permissions and
00015  * limitations under the License.
00016  */
00017 
00018 #if MBED_CONF_CELLULAR_USE_SMS
00019 
00020 #include <time.h>
00021 #include <stdlib.h>
00022 #include <stdio.h>
00023 #include "ThisThread.h"
00024 #include "AT_CellularSMS.h"
00025 #include "CellularUtil.h"
00026 #include "CellularLog.h"
00027 
00028 using namespace mbed_cellular_util;
00029 using namespace mbed;
00030 using namespace std;
00031 using namespace rtos;
00032 
00033 #define CTRL_Z  "\x1a"
00034 #define ESC     "\x1b"
00035 
00036 const uint8_t SMS_STATUS_SIZE = 12 + 1;
00037 const uint8_t FIRST_OCTET_DELIVER_SUBMIT = 17;
00038 const uint8_t TP_VALIDITY_PERIOD_24_HOURS = 167;
00039 const uint8_t TP_PROTOCOL_IDENTIFIER = 0;
00040 const uint8_t SMS_DATA_CODING_SCHEME = 0;
00041 
00042 const uint8_t SMS_MAX_8BIT_CONCATENATED_SINGLE_SMS_SIZE = 134;
00043 const uint8_t SMS_MAX_GSM7_CONCATENATED_SINGLE_SMS_SIZE = 153;
00044 #define NVAM '?' // Not Valid ascii, ISO-8859-1 mark
00045 
00046 // mapping table from 7-bit GSM to ascii (ISO-8859-1)
00047 static const unsigned char gsm_to_ascii[] = {
00048     64,     // 0
00049     163,    // 1
00050     36,     // 2
00051     165,    // 3
00052     232,    // 4
00053     233,    // 5
00054     249,    // 6
00055     236,    // 7
00056     242,    // 8
00057     199,    // 9
00058     10,     // 10
00059     216,    // 11
00060     248,    // 12
00061     13,     // 13
00062     197,    // 14
00063     229,    // 15
00064     NVAM,   // 16
00065     95,     // 17
00066     NVAM,   // 18
00067     NVAM,   // 19
00068     NVAM,   // 20
00069     NVAM,   // 21
00070     NVAM,   // 22
00071     NVAM,   // 23
00072     NVAM,   // 24
00073     NVAM,   // 25
00074     NVAM,   // 26
00075     27,     // 27
00076     198,    // 28
00077     230,    // 29
00078     223,    // 30
00079     201,    // 31
00080     32,     // 32
00081     33,     // 33
00082     34,     // 34
00083     35,     // 35
00084     164,    // 36
00085     37,     // 37
00086     38,     // 38
00087     39,     // 39
00088     40,     // 40
00089     41,     // 41
00090     42,     // 42
00091     43,     // 43
00092     44,     // 44
00093     45,     // 45
00094     46,     // 46
00095     47,     // 47
00096     48,     // 48
00097     49,     // 49
00098     50,     // 50
00099     51,     // 51
00100     52,     // 52
00101     53,     // 53
00102     54,     // 54
00103     55,     // 55
00104     56,     // 56
00105     57,     // 57
00106     58,     // 58
00107     59,     // 59
00108     60,     // 60
00109     61,     // 61
00110     62,     // 62
00111     63,     // 63
00112     161,    // 64
00113     65,     // 65
00114     66,     // 66
00115     67,     // 67
00116     68,     // 68
00117     69,     // 69
00118     70,     // 70
00119     71,     // 71
00120     72,     // 72
00121     73,     // 73
00122     74,     // 74
00123     75,     // 75
00124     76,     // 76
00125     77,     // 77
00126     78,     // 78
00127     79,     // 79
00128     80,     // 80
00129     81,     // 81
00130     82,     // 82
00131     83,     // 83
00132     84,     // 84
00133     85,     // 85
00134     86,     // 86
00135     87,     // 87
00136     88,     // 88
00137     89,     // 89
00138     90,     // 90
00139     196,    // 91
00140     214,    // 92
00141     209,    // 93
00142     220,    // 94
00143     167,    // 95
00144     191,    // 96
00145     97,     // 97
00146     98,     // 98
00147     99,     // 99
00148     100,    // 100
00149     101,    // 101
00150     102,    // 102
00151     103,    // 103
00152     104,    // 104
00153     105,    // 105
00154     106,    // 106
00155     107,    // 107
00156     108,    // 108
00157     109,    // 109
00158     110,    // 110
00159     111,    // 111
00160     112,    // 112
00161     113,    // 113
00162     114,    // 114
00163     115,    // 115
00164     116,    // 116
00165     117,    // 117
00166     118,    // 118
00167     119,    // 119
00168     120,    // 120
00169     121,    // 121
00170     122,    // 122
00171     228,    // 123
00172     246,    // 124
00173     241,    // 125
00174     252,    // 126
00175     224     // 127
00176 };
00177 
00178 const int GSM_TO_ASCII_TABLE_SIZE = sizeof(gsm_to_ascii) / sizeof(gsm_to_ascii[0]);
00179 
00180 AT_CellularSMS::AT_CellularSMS(ATHandler &at, AT_CellularDevice &device) : _cb(0), _mode(CellularSMSMmodeText),
00181     _use_8bit_encoding(false), _sim_wait_time(0), _sms_message_ref_number(1),
00182     _sms_info(NULL), _at(at), _device(device)
00183 {
00184 }
00185 
00186 AT_CellularSMS::~AT_CellularSMS()
00187 {
00188 }
00189 
00190 void AT_CellularSMS::cmt_urc()
00191 {
00192     tr_debug("CMT_URC called");
00193     //+CMT: <oa>,[<alpha>],<scts>[,<tooa>,<fo>,<pid>,<dcs>,<sca>,<tosca>,<length>]<CR><LF><data>
00194     (void)_at.consume_to_stop_tag();
00195     // call user defined callback function
00196     if (_cb) {
00197         _cb();
00198     } else {
00199         tr_warn("cmt_urc, no user defined callback for receiving sms!");
00200     }
00201 }
00202 
00203 void AT_CellularSMS::cmti_urc()
00204 {
00205     //+CMTI: <mem>,<index>,
00206     tr_debug("CMTI_URC called");
00207     // call user defined callback function
00208     if (_cb) {
00209         _cb();
00210     } else {
00211         tr_warn("cmti_urc, no user defined callback for receiving sms!");
00212     }
00213 }
00214 
00215 nsapi_error_t AT_CellularSMS::set_cnmi()
00216 {
00217     if (!_device.get_property(AT_CellularDevice::PROPERTY_AT_CNMI)) {
00218         return NSAPI_ERROR_UNSUPPORTED ;
00219     }
00220 
00221     return _at.at_cmd_discard("+CNMI", "=2,1");
00222 }
00223 
00224 nsapi_error_t AT_CellularSMS::set_cmgf(int msg_format)
00225 {
00226     if (!_device.get_property(AT_CellularDevice::PROPERTY_AT_CMGF)) {
00227         return NSAPI_ERROR_UNSUPPORTED ;
00228     }
00229 
00230     return _at.at_cmd_discard("+CMGF", "=", "%d", msg_format);
00231 }
00232 
00233 nsapi_error_t AT_CellularSMS::set_csmp(int fo, int vp, int pid, int dcs)
00234 {
00235     if (!_device.get_property(AT_CellularDevice::PROPERTY_AT_CSMP)) {
00236         return NSAPI_ERROR_UNSUPPORTED ;
00237     }
00238 
00239     return _at.at_cmd_discard("+CSMP", "=", "%d%d%d%d", fo, vp, pid, dcs);
00240 }
00241 
00242 nsapi_error_t AT_CellularSMS::set_csdh(int show_header)
00243 {
00244     if (!_device.get_property(AT_CellularDevice::PROPERTY_AT_CSDH)) {
00245         return NSAPI_ERROR_UNSUPPORTED ;
00246     }
00247 
00248     return _at.at_cmd_discard("+CSDH", "=", "%d", show_header);
00249 }
00250 
00251 nsapi_error_t AT_CellularSMS::initialize(CellularSMSMmode mode,
00252                                          CellularSMSEncoding encoding)
00253 {
00254     _use_8bit_encoding = (encoding == CellularSMSEncoding8Bit);
00255 
00256     _at.set_urc_handler("+CMTI:", callback(this, &AT_CellularSMS::cmti_urc));
00257     _at.set_urc_handler("+CMT:", callback(this, &AT_CellularSMS::cmt_urc));
00258 
00259     _at.lock();
00260     set_cnmi();     //set new SMS indication
00261     set_cmgf(mode); //set message format/PDU
00262 
00263     if (mode == CellularSMSMmodeText) {
00264         set_csmp(FIRST_OCTET_DELIVER_SUBMIT, TP_VALIDITY_PERIOD_24_HOURS, TP_PROTOCOL_IDENTIFIER,
00265                  SMS_DATA_CODING_SCHEME);    //set Set Text Mode Parameters(default values for SMS-SUBMIT and RECEIVE)
00266         set_csdh(1);//set header extra info as it's needed
00267     }
00268 
00269     _mode = mode;
00270 
00271     return _at.unlock_return_error();
00272 }
00273 
00274 void AT_CellularSMS::set_extra_sim_wait_time(int sim_wait_time)
00275 {
00276     _sim_wait_time = sim_wait_time;
00277 }
00278 
00279 char *AT_CellularSMS::create_pdu(const char *phone_number, const char *message, uint8_t message_length, uint8_t msg_parts,
00280                                  uint8_t msg_part_number, uint8_t &header_size)
00281 {
00282     int totalPDULength = 0;
00283     const bool is_number_international = (phone_number[0] == '+');
00284     if (is_number_international) {
00285         ++phone_number;
00286     }
00287     int number_len = strlen(phone_number);
00288 
00289     totalPDULength += number_len;
00290     if (number_len & 0x01) { // if phone number length is not even length we must pad it and so +1
00291         totalPDULength += 1;
00292     }
00293 
00294     totalPDULength += 16; // all other than phone number and message length
00295     if (msg_parts > 1) {// add more space for UDH
00296         totalPDULength += 12;
00297     }
00298     // there might be need for padding so some more space
00299     totalPDULength += 2;
00300 
00301     // 8-bit message, converted to hex so it will take twice as much space
00302     totalPDULength += message_length * 2;
00303 
00304     // terminating nullbyte, because callers use strlen() to find out PDU size
00305     totalPDULength += 1;
00306 
00307     char *pdu = new char[totalPDULength];
00308     memset(pdu, 0, totalPDULength);
00309     int x = 0;
00310     // See more how to create PDU from 3GPP specification 23040
00311     // first two define that we use service center number which is set with +CSCA
00312     pdu[x++] = '0';
00313     pdu[x++] = '0';
00314     // First Octet of the TPDU. 41 means SMS SUBMIT, no validity period, no status report, use User Data Header.
00315     // 01 means SMS SUBMIT, no validity period, no status report, NO User Data Header.
00316     if (msg_parts > 1) { // concatenated, must use UDH
00317         pdu[x++] = '4';
00318     } else {
00319         pdu[x++] = '0';
00320     }
00321     pdu[x++] = '1';
00322     // assign a message reference automatically. We have defined TP-RD bit as 0 so duplicates are not rejected.
00323     pdu[x++] = '0';
00324     pdu[x++] = '0';
00325     // [6] and [7] Length of the Destination Phone Number
00326     int_to_hex_str(number_len, pdu + x);
00327     x += 2;
00328     // Type of the Destination Phone Number
00329     if (is_number_international) {
00330         pdu[x++] = '9'; // international
00331     } else {
00332         pdu[x++] = '8'; // unknown
00333     }
00334     pdu[x++] = '1';
00335 
00336     // phone number as reverse nibble encoded
00337     int i = 0;
00338     for (; i < number_len; i += 2) {
00339         if (i + 1 == number_len) {
00340             pdu[x++] = 'f';
00341         } else {
00342             pdu[x++] = phone_number[i + 1];
00343         }
00344         pdu[x++] = phone_number[i];
00345     }
00346 
00347     // Protocol Identifier
00348     pdu[x++] = '0';
00349     pdu[x++] = '0';
00350     // Data Coding Scheme, GSM 7-bit default alphabet = '00', 8-bit '04'
00351     pdu[x++] = '0';
00352 
00353     if (_use_8bit_encoding) {
00354         pdu[x++] = '4';
00355     } else {
00356         pdu[x++] = '0';
00357     }
00358 
00359     uint8_t udhlen = 0;
00360     // Length can be update after we have created PDU, store position for later use.
00361     int lengthPos = x;
00362     x += 2;
00363 
00364     int paddingBits = 0;
00365     if (msg_parts > 1) { // concatenated, must use UDH
00366         // user data header length in chars
00367         pdu[x++] = '0';
00368         udhlen = 6; // udh length in chars (5) + udhl length in chars
00369         pdu[x++] = '5';
00370         // Information element identifier
00371         pdu[x++] = '0';
00372         pdu[x++] = '0';
00373         //  Information element data length
00374         pdu[x++] = '0';
00375         pdu[x++] = '3';
00376         //  A reference number (must be the same for all parts of the same larger messages)
00377         int_to_hex_str(_sms_message_ref_number & 0xFF, pdu + x);
00378         x += 2;
00379         // How many parts does this message have?
00380         int_to_hex_str(msg_parts, pdu + x);
00381         x += 2;
00382         // this is a part number
00383         int_to_hex_str(msg_part_number, pdu + x);
00384         x += 2;
00385 
00386         // if there is padding bits then udhlen is octet bigger as we need to keep septet boundary
00387         paddingBits = (udhlen * 8) % 7;
00388         if (paddingBits) {
00389             paddingBits = 7 - paddingBits;
00390             udhlen += 1;
00391         }
00392     }
00393 
00394     if (_use_8bit_encoding) {
00395         char_str_to_hex_str(message, message_length, pdu + x);
00396     } else {
00397         // we might need to send zero length sms
00398         if (message_length) {
00399             if (pack_7_bit_gsm_and_hex(message, message_length, pdu + x, paddingBits) == 0) {
00400                 delete [] pdu;
00401                 return NULL;
00402             }
00403         }
00404     }
00405 
00406     // now we know the correct length of the UDL (User Data Length)
00407     int_to_hex_str(message_length + udhlen, pdu + lengthPos);
00408     header_size = x;
00409 
00410     return pdu;
00411 }
00412 
00413 nsapi_size_or_error_t AT_CellularSMS::send_sms(const char *phone_number, const char *message, int msg_len)
00414 {
00415     int single_sms_max_length = _use_8bit_encoding ?
00416                                 SMS_MAX_SIZE_8BIT_SINGLE_SMS_SIZE :
00417                                 SMS_MAX_SIZE_GSM7_SINGLE_SMS_SIZE;
00418     if ((_mode == CellularSMSMmodeText && msg_len > single_sms_max_length) || !phone_number) {
00419         return NSAPI_ERROR_PARAMETER ;
00420     }
00421 
00422     _at.lock();
00423 
00424     int write_size = 0;
00425 
00426     ThisThread::sleep_for(_sim_wait_time);
00427 
00428     if (_mode == CellularSMSMmodeText) {
00429         _at.cmd_start_stop("+CMGS", "=", "%s", phone_number);
00430 
00431         ThisThread::sleep_for(_sim_wait_time);
00432         _at.resp_start("> ", true);
00433 
00434         if (_at.get_last_error() == NSAPI_ERROR_OK ) {
00435             write_size = _at.write_bytes((uint8_t *)message, msg_len);
00436             if (write_size < msg_len) {
00437                 // sending can be cancelled by giving <ESC> character (IRA 27).
00438                 _at.cmd_start(ESC);
00439                 _at.cmd_stop();
00440                 _at.unlock();
00441                 return write_size;
00442             }
00443             // <ctrl-Z> (IRA 26) must be used to indicate the ending of the message body.
00444             _at.cmd_start(CTRL_Z);
00445             _at.cmd_stop();
00446             _at.resp_start("+CMGS:");
00447             _at.resp_stop();
00448         }
00449     } else {
00450         // supports uncompressed 8 bit data and GSM 7 bit default alphabet data. Current implementation uses only
00451         // GSM 7 bit default but support is done for 8 bit data.
00452         int sms_count;
00453         int concatenated_sms_length = _use_8bit_encoding ?
00454                                       SMS_MAX_8BIT_CONCATENATED_SINGLE_SMS_SIZE :
00455                                       SMS_MAX_GSM7_CONCATENATED_SINGLE_SMS_SIZE;
00456 
00457         if (msg_len <= single_sms_max_length) {
00458             // single message
00459             sms_count = 1;
00460         } else {
00461             // concatenated message
00462             sms_count = msg_len / concatenated_sms_length;
00463             if (msg_len % concatenated_sms_length != 0) {
00464                 sms_count++;
00465             }
00466         }
00467 
00468         int remaining_len = msg_len;
00469         int pdu_len;
00470         int msg_write_len = 0;
00471         uint8_t header_len;
00472         char *pdu_str;
00473         for (int i = 0; i < sms_count; i++) {
00474 
00475             header_len = 0;
00476             if (sms_count == 1) {
00477                 pdu_len = msg_len;
00478             } else {
00479                 pdu_len = remaining_len > concatenated_sms_length ? concatenated_sms_length : remaining_len;
00480             }
00481 
00482             pdu_str = create_pdu(phone_number, message + i * concatenated_sms_length, pdu_len,
00483                                  sms_count, i + 1, header_len);
00484             if (!pdu_str) {
00485                 _at.unlock();
00486                 return NSAPI_ERROR_NO_MEMORY ;
00487             }
00488             pdu_len = strlen(pdu_str);
00489 
00490             // specification says that service center number should not be included so we subtract -2 from pdu_len as we use '00' for automatic service center number
00491 
00492             _at.cmd_start_stop("+CMGS", "=", "%d", (pdu_len - 2) / 2);
00493 
00494             ThisThread::sleep_for(_sim_wait_time);
00495             _at.resp_start("> ", true);
00496 
00497             if (_at.get_last_error() == NSAPI_ERROR_OK ) {
00498                 write_size = _at.write_bytes((uint8_t *)pdu_str, pdu_len);
00499                 if (write_size < pdu_len) {
00500                     // calculate exact size of what we have send
00501                     if (write_size <= header_len) {
00502                         // managed only to write header or some of it so actual msg write size in this iteration is 0
00503                         write_size = 0;
00504                     } else {
00505                         write_size = (write_size - header_len) / 2; // as hex encoded so divide by two
00506                     }
00507                     msg_write_len += write_size;
00508 
00509                     // sending can be cancelled by giving <ESC> character (IRA 27).
00510                     _at.cmd_start(ESC);
00511                     _at.cmd_stop();
00512                     _at.unlock();
00513                     delete [] pdu_str;
00514                     return msg_write_len;
00515                 }
00516 
00517                 // <ctrl-Z> (IRA 26) must be used to indicate the ending of the message body.
00518                 _at.cmd_start(CTRL_Z);
00519                 _at.cmd_stop();
00520                 _at.resp_start("+CMGS:");
00521                 _at.resp_stop();
00522             }
00523             delete [] pdu_str;
00524             remaining_len -= concatenated_sms_length;
00525             if (_at.get_last_error() != NSAPI_ERROR_OK ) {
00526                 return _at.unlock_return_error();
00527             }
00528         }
00529     }
00530 
00531     _sms_message_ref_number++;
00532     nsapi_error_t ret = _at.get_last_error();
00533     _at.unlock();
00534 
00535     return (ret == NSAPI_ERROR_OK ) ? msg_len : ret;
00536 }
00537 
00538 void AT_CellularSMS::set_sms_callback(Callback<void()> func)
00539 {
00540     _cb = func;
00541 }
00542 
00543 nsapi_error_t AT_CellularSMS::set_cpms(const char *memr, const char *memw, const char *mems)
00544 {
00545     return _at.at_cmd_discard("+CPMS", "=", "%s%s%s", memr, memw, mems);
00546 }
00547 
00548 nsapi_error_t AT_CellularSMS::set_csca(const char *sca, int type)
00549 {
00550     return _at.at_cmd_discard("+CSCA", "=", "%s%d", sca, type);
00551 }
00552 
00553 nsapi_size_or_error_t AT_CellularSMS::set_cscs(const char *chr_set)
00554 {
00555     return _at.at_cmd_discard("+CSCS", "=", "%s", chr_set);
00556 }
00557 
00558 nsapi_error_t AT_CellularSMS::delete_sms(sms_info_t *sms)
00559 {
00560     _at.lock();
00561     for (int i = 0; i < sms->parts; i++) {
00562         _at.at_cmd_discard("+CMGD", "=", "%d", sms->msg_index[i]);
00563     }
00564 
00565     return _at.unlock_return_error();
00566 }
00567 
00568 // we need this as for example concatenated sms can get different sms reference numbers
00569 // if for example last part take much more time to arrive. This situation happened while testing.
00570 // What this means that after this we can't read another sms because we always read the oldest sms
00571 // that was corrupted. So we need to have delete all messages.
00572 nsapi_error_t AT_CellularSMS::delete_all_messages()
00573 {
00574     return _at.at_cmd_discard("+CMGD", "=1,4");
00575 }
00576 
00577 // read msg in text mode
00578 nsapi_size_or_error_t AT_CellularSMS::read_sms_from_index(int msg_index, char *buf, uint16_t len, char *phone_num,
00579                                                           char *time_stamp)
00580 {
00581     /*
00582      * +CMGR: <stat>,<oa>,<alpha>,<scts>[,<tooa>,<fo>,<pid>,<dcs>,<sca>,<tosca>,<length>]<CR><LF><data><CR><LF>OK<CR><LF>
00583      */
00584     ThisThread::sleep_for(_sim_wait_time);
00585     _at.cmd_start_stop("+CMGR", "=", "%d", msg_index);
00586 
00587     // TODO: NOTE:    If the selected <mem1> can contain different types of SMs (e.g. SMS-DELIVERs, SMS-SUBMITs, SMS-STATUS-REPORTs and SMS-COMMANDs),
00588     // the response may be a mix of the responses of different SM types. TE application can recognize the response format by examining the third response parameter.
00589     // for now we support sms reading of received messages
00590     int buf_len = 0;
00591     if (_at.get_last_error() == NSAPI_ERROR_OK ) {
00592         char status[SMS_STATUS_SIZE];
00593         // first we read msg status and with that we can decide how the rest of message format
00594         _at.resp_start("+CMGR:");
00595 
00596         if (_at.info_resp()) {
00597             _at.read_string(status, SMS_STATUS_SIZE);
00598             uint16_t status_len = strlen(status);
00599             if (((status_len == sizeof("REC READ") - 1) && memcmp("REC READ", status, status_len) == 0)
00600                     || ((status_len == sizeof("REC UNREAD") - 1) && memcmp("REC UNREAD", status, status_len) == 0)) {
00601                 // Received message
00602                 if (phone_num) {
00603                     _at.read_string(phone_num, SMS_MAX_PHONE_NUMBER_SIZE);
00604                 } else {
00605                     _at.skip_param(); // <oa>,<alpha>
00606                 }
00607                 _at.skip_param(); // <alpha>
00608                 if (time_stamp) {
00609                     int len = _at.read_string(time_stamp, SMS_MAX_TIME_STAMP_SIZE);
00610                     if (len < (SMS_MAX_TIME_STAMP_SIZE - 2)) {
00611                         time_stamp[len++] = ',';
00612                         _at.read_string(&time_stamp[len], SMS_MAX_TIME_STAMP_SIZE - len);
00613                     }
00614                 }
00615                 (void)_at.consume_to_stop_tag(); // consume until <CR><LF>
00616                 if (buf) {
00617                     _at.read_string(buf, len);
00618                     buf_len = strlen(buf);
00619                 }
00620             }
00621         }
00622         _at.resp_stop();
00623     }
00624 
00625     return (_at.get_last_error() == NSAPI_ERROR_OK ) ? buf_len : _at.get_last_error();
00626 }
00627 
00628 // read msg in PDU mode
00629 nsapi_size_or_error_t AT_CellularSMS::read_sms(sms_info_t *sms, char *buf, char *phone_num, char *time_stamp)
00630 {
00631     // +CMGR: <stat>,[<alpha>],<length><CR><LF><pdu>
00632     int index;
00633     if (sms->parts == sms->parts_added) {
00634         char *pdu; // we need a temp buffer as payload is hexencoded ---> can't use buf as it might be enough for message but not hexenconded pdu.
00635         int status;
00636         int msg_len;
00637         index = 0;
00638         int pduSize;
00639 
00640         for (int i = 0; i < sms->parts; i++) {
00641             ThisThread::sleep_for(_sim_wait_time);
00642             _at.cmd_start_stop("+CMGR", "=", "%d", sms->msg_index[i]);
00643             _at.resp_start("+CMGR:");
00644 
00645             if (_at.info_resp()) {
00646                 status = _at.read_int();
00647                 _at.skip_param(); // <alpha>
00648                 if ((_at.get_last_error() == NSAPI_ERROR_OK ) && (status == 0 || status == 1)) {
00649                     msg_len = _at.read_int();
00650                     if (msg_len > 0) {
00651                         pduSize = msg_len * 2 + 20; // *2 as it's hex encoded and +20 as service center number is not included in size given by CMGR
00652                         pdu = new char[pduSize];
00653                         memset(pdu, 0, pduSize);
00654                         if (!pdu) {
00655                             _at.resp_stop();
00656                             return NSAPI_ERROR_NO_MEMORY ;
00657                         }
00658                         _at.read_string(pdu, pduSize, true);
00659                         if (_at.get_last_error() == NSAPI_ERROR_OK ) {
00660                             msg_len = get_data_from_pdu(pdu, NULL, NULL, phone_num, buf + index);
00661                             if (msg_len >= 0) { // we need to allow zero length messages
00662                                 index += msg_len;
00663                             } else {
00664                                 delete [] pdu;
00665                                 _at.resp_stop();
00666                                 return -1;
00667                             }
00668                         }
00669                         delete [] pdu;
00670                     }
00671                 }
00672             }
00673             _at.resp_stop();
00674         }
00675 
00676         if (_at.get_last_error() == NSAPI_ERROR_OK ) {
00677             if (time_stamp) {
00678                 strcpy(time_stamp, sms->date);
00679             }
00680             buf[index] = '\0';
00681         }
00682     } else {
00683         tr_warn("NOT all concatenated parts were received...");
00684         index = SMS_ERROR_MULTIPART_ALL_PARTS_NOT_READ;
00685     }
00686 
00687     return index;
00688 }
00689 
00690 nsapi_size_or_error_t AT_CellularSMS::get_sms(char *buf, uint16_t len, char *phone_num, uint16_t phone_len,
00691                                               char *time_stamp, uint16_t time_len, int *buf_size)
00692 {
00693     // validate buffer sizes already here to avoid any necessary function calls and locking of _at
00694     if ((phone_num && phone_len < SMS_MAX_PHONE_NUMBER_SIZE) || (time_stamp && time_len < SMS_MAX_TIME_STAMP_SIZE) ||
00695             buf == NULL) {
00696         return NSAPI_ERROR_PARAMETER ;
00697     }
00698 
00699     _at.lock();
00700 
00701     nsapi_size_or_error_t err = list_messages();
00702     if (err == NSAPI_ERROR_OK ) {
00703         // we return the oldest sms and delete it after successful read
00704         sms_info_t *info = get_oldest_sms_index();
00705 
00706         if (info) {
00707             if (info->msg_size > len) {
00708                 tr_warn("Given buf too small, len is: %d but is must be: %d", len, info->msg_size);
00709                 if (buf_size) {
00710                     *buf_size = info->msg_size;
00711                 }
00712                 free_linked_list();
00713                 _at.unlock();
00714                 return NSAPI_ERROR_PARAMETER ;
00715             }
00716 
00717             if (_mode == CellularSMSMmodePDU) {
00718                 err = read_sms(info, buf, phone_num, time_stamp);
00719             } else {
00720                 err = read_sms_from_index(info->msg_index[0], buf, len, phone_num, time_stamp);
00721             }
00722 
00723             if (err > 0) {
00724                 int delerr = delete_sms(info);
00725                 if (delerr) {
00726                     err = delerr;
00727                 }
00728             }
00729         } else {
00730             // No messages were found, return -1
00731             err = -1;
00732         }
00733     }
00734 
00735     free_linked_list();
00736 
00737     _at.unlock();
00738 
00739     // update error only when there really was an error, otherwise we return the length
00740     if (_at.get_last_error()) {
00741         err = _at.get_last_error();
00742     }
00743     return err;
00744 }
00745 
00746 nsapi_size_or_error_t AT_CellularSMS::get_data_from_pdu(const char *pdu, sms_info_t *info, int *part_number,
00747                                                         char *phone_number, char *msg)
00748 {
00749     int index = 0;
00750     int tmp;
00751     bool userDataHeader;
00752     int oaLength;
00753     int dataScheme;
00754     nsapi_size_or_error_t err = NSAPI_ERROR_OK ;
00755 
00756     // read Length of the SMSC information
00757     oaLength = hex_str_to_int(pdu, 2);
00758     index += 2; // length we just read
00759     index += oaLength * 2; // skip service center number
00760 
00761     // read first the lower part of first octet as there is message type
00762     index++;
00763     tmp = hex_str_to_int(pdu + index, 1);
00764     //ThisThread::sleep_for(200);
00765     if ((tmp & 0x03) == 0) {// SMS-DELIVER type, last two bits should be zero
00766         // UDH present? Check from first octets higher part
00767         tmp = hex_str_to_int(pdu + (--index), 1);
00768         userDataHeader = ((tmp & 0x04) == 0) ? false : true;
00769 
00770         index += 2; // we just read the high bits of first octet so move +2
00771         // originating address length
00772         oaLength = hex_str_to_int(pdu + index, 2);
00773         index += 2; // add  index over address length
00774         int type = hex_str_to_int(pdu + index, 1);
00775         index += 2; // add  index over type
00776         if (phone_number) {
00777             // phone number as reverse nibble encoded
00778             int a = 0, field_length = oaLength;
00779 
00780             if (type == 9) {
00781                 //add the plus sign in case of international number (23.040 chapter address fields)
00782                 phone_number[a++] = '+';
00783                 field_length++;
00784             }
00785 
00786             for (; a < field_length; a += 2) {
00787                 if ((a + 1) == field_length) {
00788                     phone_number[a] = pdu[index + 1];
00789                     index++;
00790                 } else {
00791                     phone_number[a] = pdu[index + 1];
00792                     phone_number[a + 1] = pdu[index];
00793                     index += 2;
00794                 }
00795             }
00796             phone_number[field_length] = '\0';
00797         } else {
00798             index += oaLength;
00799         }
00800 
00801 
00802         if (oaLength & 0x01) { // if phone number length is odd then it has padded F so skip that
00803             index++;
00804         }
00805         index += 2; // skip TP-Protocol identifier
00806 
00807         dataScheme = hex_str_to_int(pdu + index, 2);
00808         index += 2; // skip TP-Data-Coding-Scheme
00809 
00810         // next one is date, it's length is 7 octets according to 3GPP TS 23.040
00811         // create time string
00812         if (info) {
00813             int i = 0;
00814             // year
00815             info->date[i++] = pdu[index + 1];
00816             info->date[i++] = pdu[index];
00817             index += 2;
00818             info->date[i++] = '/';
00819             // month
00820             info->date[i++] = pdu[index + 1];
00821             info->date[i++] = pdu[index];
00822             index += 2;
00823             info->date[i++] = '/';
00824             // Day
00825             info->date[i++] = pdu[index + 1];
00826             info->date[i++] = pdu[index];
00827             index += 2;
00828             info->date[i++] = ',';
00829             // Hour
00830             info->date[i++] = pdu[index + 1];
00831             info->date[i++] = pdu[index];
00832             index += 2;
00833             info->date[i++] = ':';
00834             // Minute
00835             info->date[i++] = pdu[index + 1];
00836             info->date[i++] = pdu[index];
00837             index += 2;
00838             info->date[i++] = ':';
00839             // Second
00840             info->date[i++] = pdu[index + 1];
00841             info->date[i++] = pdu[index];
00842             index += 2;
00843             // timezone related to GMT. pdu[index+1] most significant bit indicates the sign related to gmt
00844             tmp = hex_str_to_int(pdu + index + 1, 1);
00845             if (tmp & 0x08) {
00846                 info->date[i++] = '-';
00847             } else {
00848                 info->date[i++] = '+';
00849             }
00850 
00851             // pdu[index+1 & 0x07 is the most significant bits of the timezone
00852             // pdu [index] is the least significant bits
00853             info->date[i++] = '0' + (tmp & 0x07);
00854             info->date[i++] = pdu[index];
00855             info->date[i] = '\0';
00856             index += 2;
00857         } else {
00858             index += 14;
00859         }
00860 
00861         int udl =  hex_str_to_int(pdu + index, 2);
00862         index += 2;
00863 
00864         int paddingBits = 0;
00865         int partnro = 1;
00866         if (userDataHeader) {
00867             // we need to read User Defined Header to know what part number this message is.
00868             index += read_udh_from_pdu(pdu + index, info, partnro, paddingBits);
00869         }
00870 
00871         if (part_number) {
00872             *part_number = partnro;
00873         }
00874 
00875         if (msg) {
00876             // we are reading the message
00877             err = read_pdu_payload(pdu + index, udl, dataScheme, msg, paddingBits);
00878         } else {
00879             if (dataScheme == 0x00) {
00880                 // when listing messages we need to calculated length. Other way would be unpacking the whole message.
00881                 err = strlen(pdu + index) >> 1;
00882                 err *= 8;
00883                 err /= 7;
00884             } else if (dataScheme == 0x04) {
00885                 err = strlen(pdu + index) >> 1;
00886             } else {
00887                 return NSAPI_ERROR_UNSUPPORTED ;
00888             }
00889         }
00890 
00891         return err;
00892     } else {
00893         // message was not DELIVER so discard it
00894         return NSAPI_ERROR_UNSUPPORTED ;
00895     }
00896 }
00897 
00898 // read params from User Defined Header
00899 int AT_CellularSMS::read_udh_from_pdu(const char *pdu, sms_info_t *info, int &part_number, int &padding_bits)
00900 {
00901 
00902     int index = 0;
00903     int udhLength = hex_str_to_int(pdu, 2);
00904     index += 2;
00905 
00906     // if there is padding bits then udhlen is octet bigger as we need to keep septet boundary
00907     padding_bits = ((udhLength + 1) * 8) % 7; // +1 is for udhLength itself
00908 
00909     if (padding_bits) {
00910         padding_bits = 7 - padding_bits;
00911     } else {
00912         padding_bits = 0;
00913     }
00914 
00915     int tmp = hex_str_to_int(pdu + index, 2);
00916     index += 4;
00917 
00918     if (tmp == 0) { // 8-bit reference number
00919         if (info) {
00920             info->msg_ref_number = (uint16_t)hex_str_to_int(pdu + index, 2);
00921         }
00922         index += 2;
00923     } else {                  // 16-bit reference number
00924         if (info) {
00925             info->msg_ref_number = (uint16_t)hex_str_to_int(pdu + index + 2, 2);
00926             tmp = hex_str_to_int(pdu + index, 2);
00927             info->msg_ref_number |= (tmp << 8);
00928         }
00929         index += 4;
00930     }
00931 
00932     if (info) {
00933         info->parts = hex_str_to_int(pdu + index, 2);
00934     }
00935     index += 2;
00936 
00937     part_number = hex_str_to_int(pdu + index, 2);
00938     index += 2;
00939 
00940     return (udhLength * 2 + 2); // udh in hex and udhl
00941 }
00942 
00943 nsapi_size_or_error_t AT_CellularSMS::read_pdu_payload(const char *pdu, int msg_len, int scheme, char *msg, int padding_bits)
00944 {
00945     if (scheme == 0x00) {
00946         // 7 bit gsm encoding, must do the conversions from hex to 7-bit encoding and to ascii
00947         return unpack_7_bit_gsm_to_str(pdu, strlen(pdu) / 2, msg, padding_bits, msg_len);
00948     } else if (scheme == 0x04) {
00949         // 8bit scheme so just convert hexstring to charstring
00950         return hex_str_to_char_str(pdu, strlen(pdu), msg);
00951     } else {
00952         tr_error("Received unsupported data coding scheme: 0x%02x", scheme);
00953         return NSAPI_ERROR_UNSUPPORTED ;
00954     }
00955 }
00956 
00957 void AT_CellularSMS::free_linked_list()
00958 {
00959     sms_info_t *info = _sms_info;
00960     sms_info_t *old;
00961     while (info) {
00962         old = info;
00963         info = info->next_info;
00964         delete old;
00965     }
00966     _sms_info = NULL;
00967 }
00968 
00969 void AT_CellularSMS::add_info(sms_info_t *info, int index, int part_number)
00970 {
00971     // check for same message reference id. If found, update it and delete the given info.
00972     // if NOT found then add to the end of the list.
00973 
00974     if (!_sms_info) {
00975         info->msg_index[part_number - 1] = index; // part numbering starts from 1 so -1 to put to right index
00976         _sms_info = info;
00977         return;
00978     }
00979     sms_info_t *current = _sms_info;
00980     sms_info_t *prev = NULL;
00981     bool found_msg = false;
00982     while (current) {
00983         prev = current;
00984         // sms messages can have same reference number so additional checks are needed.
00985         // TODO: should we include phone number also?
00986         if (current->msg_ref_number == info->msg_ref_number && current->parts > current->parts_added &&
00987                 info->parts > info->parts_added) {
00988             // multipart sms, update msg size and index
00989             current->msg_size += info->msg_size;
00990             current->msg_index[part_number - 1] = index; // part numbering starts from 1 so -1 to put to right index
00991             current->parts_added++;
00992             // update oldest part as date
00993             if (compare_time_strings(info->date, current->date) == -1) {
00994                 strcpy(current->date, info->date);
00995             }
00996             found_msg = true;
00997             break;
00998         }
00999         current = current->next_info;
01000     }
01001 
01002     if (found_msg) {
01003         // info was added to existing item in linked list, must be deleted
01004         delete info;
01005     } else {
01006         // message not found, add to linked list
01007         info->msg_index[part_number - 1] = index;
01008         prev->next_info = info;
01009     }
01010 }
01011 
01012 // reads all the messages to the linked list AT_CellularSMS::_sms_info
01013 nsapi_error_t AT_CellularSMS::list_messages()
01014 {
01015     // TODO: NOTE:  If the selected <mem1> can contain different types of SMs (e.g. SMS-DELIVERs, SMS-SUBMITs, SMS-STATUS-REPORTs and SMS-COMMANDs),
01016     // the response may be a mix of the responses of different SM types. TE application can recognize the response format by examining the third response parameter.
01017     // for now we assume that only SMS-DELIVER messages are read.
01018     if (_mode == CellularSMSMmodePDU) {
01019         _at.cmd_start_stop("+CMGL", "=4");
01020     } else {
01021         _at.cmd_start_stop("+CMGL", "=\"ALL\"");
01022     }
01023 
01024     sms_info_t *info = NULL;
01025     // init for 1 so that in text mode we will add to the correct place without any additional logic in addInfo() in text mode
01026     int part_number = 1;
01027     int index = 0;
01028     int length = 0;
01029     char *pdu = NULL;
01030 
01031     _at.resp_start("+CMGL:");
01032     while (_at.info_resp()) {
01033         info = new sms_info_t();
01034         if (_mode == CellularSMSMmodePDU) {
01035             //+CMGL: <index>,<stat>,[<alpha>],<length><CR><LF><pdu>[<CR><LF>
01036             // +CMGL:<index>,<stat>,[<alpha>],<length><CR><LF><pdu>
01037             //[...]]
01038             index = _at.read_int();
01039             _at.skip_param(2); // <stat>,[<alpha>]
01040             length = _at.read_int();
01041             length = length * 2 + 20; // *2 as it's hex encoded and +20 as service center number is not included in size given by CMGL
01042             pdu = new char[length];
01043             memset(pdu, 0, length);
01044             _at.read_string(pdu, length, true);
01045             if (_at.get_last_error() == NSAPI_ERROR_OK ) {
01046                 info->msg_size = get_data_from_pdu(pdu, info, &part_number);
01047             }
01048         } else {
01049             // +CMGL: <index>,<stat>,<oa/da>,[<alpha>],[<scts>][,<tooa/toda>,<length>]<CR><LF><data>[<CR><LF>
01050             // +CMGL: <index>,<stat>,<da/oa>,[<alpha>],[<scts>][,<tooa/toda>,<length>]<CR><LF><data>[...]]
01051             index = _at.read_int();
01052             (void)_at.consume_to_stop_tag(); // consume until <CR><LF>
01053             (void)_at.consume_to_stop_tag(); // consume until <CR><LF>
01054         }
01055 
01056         if (index >= 0) {
01057             add_info(info, index, part_number);
01058         } else {
01059             delete info;
01060             info = NULL;
01061         }
01062         delete [] pdu;
01063         pdu = NULL;
01064     }
01065 
01066 
01067     _at.resp_stop();
01068 
01069     return _at.get_last_error();
01070 }
01071 
01072 AT_CellularSMS::sms_info_t *AT_CellularSMS::get_oldest_sms_index()
01073 {
01074     /*
01075      * Different scenarios when finding the oldest concatenated sms
01076      *
01077      * 1. Find first parts first and it was received first
01078      * 2. Find first parts first and it was NOT received first -> older timestamp might exist in some other part
01079      * 3. Find other than first part first and it was received first
01080      * 4. Find other than first part first and it was NOT received first -> older timestamp might exist in some other part
01081      *
01082      * So must take all message to a linked list and loop that for the oldest
01083      */
01084 
01085     // if text mode we need to read sms with +CMGR because time stamp is optional while looping with +CMGL
01086     sms_info_t *retVal = NULL;
01087     sms_info_t *current = _sms_info;
01088     nsapi_size_or_error_t err = 0;
01089     while (current) {
01090         if (_mode == CellularSMSMmodeText) {
01091             ThisThread::sleep_for(_sim_wait_time);
01092             err = read_sms_from_index(current->msg_index[0], NULL, 0, NULL, current->date);
01093             if (err != 0) {
01094                 return NULL;
01095             }
01096         }
01097 
01098         if (retVal == NULL) {
01099             retVal = current;
01100         } else if (compare_time_strings(current->date, retVal->date) == -1) {
01101             // found older sms, update return value to oldest
01102             retVal = current;
01103         }
01104         current = current->next_info;
01105     }
01106 
01107     return retVal;
01108 }
01109 
01110 // if time_string_1 is greater (more fresh date) then return 1, same 0, smaller -1. Error -2
01111 int AT_CellularSMS::compare_time_strings(const char *time_string_1, const char *time_string_2)
01112 {
01113     time_t t1;
01114     time_t t2;
01115 
01116     bool success = create_time(time_string_1, &t1) && create_time(time_string_2, &t2);
01117     int retVal = -2;
01118 
01119     if (success) {
01120         time_t diff = t1 - t2;
01121 
01122         if (diff > 0) {
01123             retVal = 1;
01124         } else if (diff == 0) {
01125             retVal = 0;
01126         } else {
01127             retVal = -1;
01128         }
01129     }
01130 
01131     return retVal;
01132 }
01133 
01134 bool AT_CellularSMS::create_time(const char *time_string, time_t *time)
01135 {
01136     const int kNumberOfElements = 8;
01137     tm time_struct = { 0 };
01138     int gmt = 0;
01139     char sign;
01140     bool retVal = false;
01141 
01142     if (sscanf(time_string, "%d/%d/%d,%d:%d:%d%c%d", &time_struct.tm_year, &time_struct.tm_mon, &time_struct.tm_mday,
01143                &time_struct.tm_hour, &time_struct.tm_min, &time_struct.tm_sec, &sign, &gmt) == kNumberOfElements) {
01144         *time = mktime(&time_struct);
01145         // add timezone as seconds. gmt is in quarter of hours.
01146         int x = (60 / 4) * 60 * gmt;
01147         if (sign == '+') {
01148             *time += x;
01149         } else {
01150             *time -= x;
01151         }
01152         retVal = true;
01153     }
01154 
01155     return retVal;
01156 }
01157 
01158 uint16_t AT_CellularSMS::pack_7_bit_gsm_and_hex(const char *str, uint16_t len, char *buf,
01159                                                 int number_of_padding_bit)
01160 {
01161     uint16_t strCnt = 0;
01162     uint16_t i = 0;
01163     uint8_t shift;
01164     char tmp;
01165 
01166     if (len == 0) {
01167         return 0;
01168     }
01169     // convert to 7bit gsm first
01170     char *gsm_str = new char[len];
01171     for (uint16_t y = 0; y < len; y++) {
01172         for (int x = 0; x < GSM_TO_ASCII_TABLE_SIZE; x++) {
01173             if (gsm_to_ascii[x] == static_cast<unsigned char>(str[y])) {
01174                 gsm_str[y] = x;
01175             }
01176         }
01177     }
01178 
01179     // then packing and converting to hex
01180     if (number_of_padding_bit) {
01181         tmp = gsm_str[strCnt] << number_of_padding_bit;
01182         strCnt++;
01183         char_str_to_hex_str(&tmp, 1, &buf[i * 2]);
01184         i++;
01185     }
01186 
01187     while (strCnt < len) {
01188         if (number_of_padding_bit) {
01189             shift = (i + number_of_padding_bit - 2) % 7;
01190         } else {
01191             shift = i % 7;
01192         }
01193 
01194         if (strCnt + 1 == len) {
01195             tmp = (gsm_str[strCnt] >> shift);
01196         } else {
01197             tmp = (gsm_str[strCnt] >> shift) | (gsm_str[strCnt + 1] << (7 - shift));
01198         }
01199 
01200         char_str_to_hex_str(&tmp, 1, buf + (i * 2));
01201 
01202         if (shift == 6) {
01203             strCnt++;
01204         }
01205         strCnt++;
01206         i++;
01207     }
01208 
01209     delete [] gsm_str;
01210 
01211     return i;
01212 }
01213 
01214 uint16_t AT_CellularSMS::unpack_7_bit_gsm_to_str(const char *str, int len, char *buf, int padding_bits,
01215                                                  int msg_len)
01216 {
01217     int strCount = 0;
01218     uint16_t decodedCount = 0;
01219     uint8_t shift;
01220     char tmp;
01221     char tmp1;
01222 
01223     if (padding_bits) {
01224         hex_to_char(str, tmp);
01225         buf[decodedCount] = gsm_to_ascii[(tmp >> padding_bits) & 0x7F];
01226         strCount++;
01227         decodedCount++;
01228     }
01229 
01230     while (strCount < len) {
01231         shift = (strCount - padding_bits) % 7;
01232         hex_to_char(str + strCount * 2, tmp);
01233         if (shift == 0) {
01234             buf[decodedCount] = gsm_to_ascii[tmp & 0x7F];
01235         } else if (shift == 6) {
01236             hex_to_char(str + (strCount - 1) * 2, tmp1);
01237             buf[decodedCount] = gsm_to_ascii[(((tmp1 >> 2)) | (tmp << 6)) & 0x7F];
01238             if (decodedCount + 1 < msg_len) {
01239                 hex_to_char(str + strCount * 2, tmp);
01240                 decodedCount++;
01241                 buf[decodedCount] = gsm_to_ascii[(tmp >> 1) & 0x7F];
01242             }
01243         } else {
01244             hex_to_char(str + (strCount - 1) * 2, tmp1);
01245             buf[decodedCount] = gsm_to_ascii[(((tmp1 >> (8 - shift))) | ((tmp << shift))) & 0x7F];
01246         }
01247 
01248         strCount++;
01249         decodedCount++;
01250     }
01251 
01252     return decodedCount;
01253 }
01254 
01255 ATHandler &AT_CellularSMS::get_at_handler()
01256 {
01257     return _at;
01258 }
01259 
01260 #endif //MBED_CONF_CELLULAR_USE_SMS