takashi kadono / Mbed OS Nucleo_446

Dependencies:   ssd1331

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