From be01eb8a87f1582b1c15ec4e09a9a66770da87c5 Mon Sep 17 00:00:00 2001 From: Brandon Bayer Date: Mon, 26 Oct 2015 14:58:43 -0500 Subject: feat: sms support for CE910-DUAL, DE910-DUAL, LE910-SVG - PDU is used for everything except LE910-SVG sending - a bug in radio requires radio reboot to send more than 1 PDU sms --- src/atcmd.c | 177 +++++++++++++++++++++++++++++++++++++++++++++++++++---- src/atcmd.h | 12 +++- src/global.h | 1 + src/pdu.h | 16 +++-- src/pdu_decode.c | 156 +++++++++++++++++++++++++++++++++++++++++++++--- src/pdu_decode.h | 4 +- src/pdu_encode.c | 125 ++++++++++++++++++++++++++++++++++----- src/pdu_encode.h | 3 +- src/sms_list.c | 9 ++- src/sms_main.c | 1 + src/sms_send.c | 44 ++++++++++++-- 11 files changed, 500 insertions(+), 48 deletions(-) diff --git a/src/atcmd.c b/src/atcmd.c index aad15e9..578554f 100644 --- a/src/atcmd.c +++ b/src/atcmd.c @@ -674,6 +674,51 @@ int atcmd_plus_cmgw_write(int fd, const char *msg, size_t msg_len) return mem_index; } +int atcmd_plus_cmgw_write_text(int fd, const char *addr, int addr_type, + const char *status, const char *msg, size_t msg_len) +{ + char buf[ATCMD_LINE_SIZE]; + int tmp; + int mem_index; + + atcmd_printf(fd, "AT+CMGW"); + if (addr) { + atcmd_printf(fd, "=\"%s\"", addr); + if (addr_type != SMS_ADDR_UNSPEC) { + atcmd_printf(fd, ",%d", addr_type); + if (status) { + atcmd_printf(fd, ",\"%s\"", status); + } + } + } + atcmd_write_str(fd, ATCMD_EOL); + + tmp = atcmd_read_until(fd, buf, sizeof(buf), "> "); + if (tmp <= 0) { + log_debug("expected > start sequence but it was not received"); + return -1; + } + + tmp = atcmd_write(fd, msg, strlen(msg)); + tmp = atcmd_write_str(fd, CONTROL_Z_STR); + + tmp = atcmd_expect_line(fd, buf, sizeof(buf), "+CMGW: "); + if (tmp <= 0) { + log_debug("expected +CMGW: but it was not received"); + return -1; + } + + mem_index = atoi(buf + strlen("+CMGW: ")); + + tmp = atcmd_expect_line(fd, buf, sizeof(buf), "OK"); + if (tmp <= 0) { + log_debug("expected OK but it was not received"); + return -1; + } + + return mem_index; +} + int atcmd_plus_cmgs_write(int fd, const char *msg, size_t msg_len) { char buf[ATCMD_LINE_SIZE]; @@ -706,6 +751,38 @@ int atcmd_plus_cmgs_write(int fd, const char *msg, size_t msg_len) return msg_ref; } +int atcmd_plus_cmgs_write_text(int fd, const char *addr, const char *msg, size_t msg_len) +{ + char buf[ATCMD_LINE_SIZE]; + int tmp; + int msg_ref; + + atcmd_writeline(fd, "AT+CMGS=\"%s\"", addr); + tmp = atcmd_read_until(fd, buf, sizeof(buf), "> "); + if (tmp <= 0) { + log_debug("expected > start sequence but it was not received"); + return -1; + } + + tmp = atcmd_write(fd, msg, strlen(msg)); + tmp = atcmd_write_str(fd, CONTROL_Z_STR); + + tmp = atcmd_expect_line(fd, buf, sizeof(buf), "+CMGS: "); + if (tmp <= 0) { + log_debug("expected +CMGS: but it was not received"); + return -1; + } + msg_ref = atoi(buf + strlen("+CMGS: ")); + + tmp = atcmd_expect_line(fd, buf, sizeof(buf), "OK"); + if (tmp <= 0) { + log_debug("expected OK but it was not received"); + return -1; + } + + return msg_ref; +} + int atcmd_plus_cmss_write(int fd, int index, const char *addr, int addr_type) { char buf[ATCMD_LINE_SIZE]; @@ -774,16 +851,24 @@ int atcmd_response_foreach_line(int fd, atcmd_response_callback_t call, void *pr } int atcmd_plus_cpms_read(int fd, struct data_store *read_store, - struct data_store *send_store, struct data_store *new_store) + struct data_store *send_store, struct data_store *new_store, const char *model) { char buf[ATCMD_LINE_SIZE]; char *save; char *token; int tmp; int i; + int data_stores_size; - struct data_store *data_stores[] = {read_store, send_store, new_store}; struct data_store *store; + struct data_store *data_stores[] = {read_store, send_store, new_store}; + + if (isCdmaTypeModel()) { + data_stores_size = 2; + } + else { + data_stores_size = 3; + } atcmd_writeline(fd, "AT+CPMS?"); tmp = atcmd_expect_line(fd, buf, sizeof(buf), "+CPMS: "); @@ -799,7 +884,7 @@ int atcmd_plus_cpms_read(int fd, struct data_store *read_store, return -1; } - for (i = 0; i < ARRAY_SIZE(data_stores); i++) { + for (i = 0; i < data_stores_size; i++) { store = data_stores[i]; token = atcmd_value_tok(&save); @@ -844,7 +929,7 @@ int atcmd_plus_cpms_test(int fd, struct store_locations *read_loc, int tmp; int i, j; - struct store_locations *locations[] = {read_loc, send_loc, new_loc}; + struct store_locations *locations[] = {read_loc, send_loc, new_loc}; struct store_locations *loc; atcmd_writeline(fd, "AT+CPMS=?"); @@ -901,13 +986,19 @@ int atcmd_plus_cpms_test(int fd, struct store_locations *read_loc, } int atcmd_plus_cpms_write(int fd, const char *read_name, - const char *send_name, const char *new_name) + const char *send_name, const char *new_name, const char *model) { char buf[ATCMD_LINE_SIZE]; int tmp; - atcmd_writeline(fd, "AT+CPMS=\"%s\",\"%s\",\"%s\"", - read_name, send_name, new_name); + if (isCdmaTypeModel()) { + atcmd_writeline(fd, "AT+CPMS=\"%s\",\"%s\"", read_name, send_name); + } + else + { + atcmd_writeline(fd, "AT+CPMS=\"%s\",\"%s\",\"%s\"", + read_name, send_name, new_name); + } tmp = atcmd_expect_line(fd, buf, sizeof(buf), "OK"); if (tmp <= 0) { log_debug("expected OK but it was not received"); @@ -1110,6 +1201,48 @@ int atcmd_plus_cpbr_test(int fd, struct phonebook_store *store) return 0; } +int atcmd_plus_gmm_read(int fd) +{ + char buf[ATCMD_LINE_SIZE]; + char *save; + char *token; + int tmp; + + atcmd_writeline(fd, "AT+GMM"); + //Swallow extra "\r\n" + tmp = atcmd_readline(fd, buf, sizeof(buf)); + if (tmp <= 0) { + log_debug("expected \\r\\n but it was not received"); + return -1; + } + + //Read model string + tmp = atcmd_readline(fd, buf, sizeof(buf)); + if (tmp <= 0) { + log_debug("expected model but it was not received"); + return -1; + } + + save = buf; + token = atcmd_value_tok(&save); + if (!token) { + log_debug("atcmd_value_tok model"); + return -1; + } + free(Global.core.model); + Global.core.model = strdup(token); + + log_debug("model: %s", Global.core.model); + + tmp = atcmd_expect_line(fd, buf, sizeof(buf), "OK"); + if (tmp <= 0) { + log_debug("expected OK but it was not received"); + return -1; + } + + return 0; +} + int atcmd_init(int fd, int read_timeout) { int tmp; @@ -1158,6 +1291,12 @@ static int sms_atcmd_init(int fd) return -1; } + tmp = atcmd_plus_gmm_read(fd); + if (tmp < 0) { + log_error("failed to read radio model"); + return -1; + } + tmp = atcmd_plus_cmgf_write(fd, SMS_PDU_MODE); if (tmp < 0) { return -1; @@ -1167,10 +1306,18 @@ static int sms_atcmd_init(int fd) memset(&send_store, 0, sizeof(send_store)); memset(&new_store, 0, sizeof(new_store)); + if (!strcmp(Global.core.model, "CE910-DUAL") || !strcmp(Global.core.model, "DE910-DUAL")) { + log_info("Changing message storage locations to ME for CE/DE910-DUAL"); + free(Global.core.msg_store_read); + free(Global.core.msg_store_send); + Global.core.msg_store_read = strdup("ME"); + Global.core.msg_store_send = strdup("ME"); + } + atcmd_plus_cpms_test(fd, &read_store.choices, &send_store.choices, &new_store.choices); atcmd_plus_cpms_read(fd, &read_store.selected, - &send_store.selected, &new_store.selected); + &send_store.selected, &new_store.selected, Global.core.model); if (Global.core.msg_store_read && Global.core.msg_store_send && Global.core.msg_store_new) { @@ -1184,14 +1331,16 @@ static int sms_atcmd_init(int fd) Global.core.msg_store_send); return -1; } - if (!msg_store_choice(&new_store, Global.core.msg_store_new)) { + + //Doesn't apply to CDMA type radios + if (!isCdmaTypeModel() && !msg_store_choice(&new_store, Global.core.msg_store_new)) { log_error("message storage location %s is not a choice", Global.core.msg_store_new); return -1; } tmp = atcmd_plus_cpms_write(fd, Global.core.msg_store_read, - Global.core.msg_store_send, Global.core.msg_store_new); + Global.core.msg_store_send, Global.core.msg_store_new, Global.core.model); if (tmp < 0) { return -1; } @@ -1255,3 +1404,11 @@ int sms_device_open(void) return fd; } + +int isCdmaTypeModel() +{ + return (!strcmp(Global.core.model, "LE910-SVG") || + !strcmp(Global.core.model, "DE910-DUAL") || + !strcmp(Global.core.model, "CE910-DUAL")); +} + diff --git a/src/atcmd.h b/src/atcmd.h index de6eeee..16b991f 100644 --- a/src/atcmd.h +++ b/src/atcmd.h @@ -62,10 +62,15 @@ int atcmd_v_write(int fd, int mode); int atcmd_q_write(int fd, int mode); int atcmd_plus_cmgf_write(int fd, int mode); int atcmd_plus_cmgw_write(int fd, const char *msg, size_t msg_len); +int atcmd_plus_cmgw_write_text(int fd, const char *addr, int addr_type, + const char *status, const char *msg, size_t msg_len); int atcmd_plus_cmgs_write(int fd, const char *msg, size_t msg_len); +int atcmd_plus_cmgs_write_text(int fd, const char *addr, const char *msg, size_t msg_len); int atcmd_plus_cmss_write(int fd, int index, const char *addr, int addr_type); int atcmd_plus_cmgd_write(int fd, int index); +int isCdmaTypeModel(); + #define STORE_NAME_LEN 2 #define STORE_NAME_SIZE (STORE_NAME_LEN + 1) #define STORE_LOCATIONS_MAX 8 @@ -87,11 +92,11 @@ struct msg_store { }; int atcmd_plus_cpms_read(int fd, struct data_store *read_store, - struct data_store *send_store, struct data_store *new_store); + struct data_store *send_store, struct data_store *new_store, const char *model); int atcmd_plus_cpms_test(int fd, struct store_locations *read_loc, struct store_locations *send_loc, struct store_locations *new_loc); int atcmd_plus_cpms_write(int fd, const char *read_name, - const char *send_name, const char *new_name); + const char *send_name, const char *new_name, const char *model); struct phonebook_store { struct data_store selected; @@ -105,9 +110,10 @@ struct phonebook_store { int atcmd_plus_cpbs_read(int fd, struct data_store *store); int atcmd_plus_cpbs_test(int fd, struct store_locations *loc); int atcmd_plus_cpbs_write(int fd, const char *name); - int atcmd_plus_cpbr_test(int fd, struct phonebook_store *store); +int atcmd_plus_gmm_read(int fd); + int atcmd_init(int fd, int read_timeout); int sms_device_close(int fd); int sms_device_open(void); diff --git a/src/global.h b/src/global.h index fe8f5c2..4248c64 100644 --- a/src/global.h +++ b/src/global.h @@ -29,6 +29,7 @@ struct global_core { char *msg_store_send; char *msg_store_new; char *pb_store; + char *model; char *editor; char *edit_file; diff --git a/src/pdu.h b/src/pdu.h index 468687d..5f47e5e 100644 --- a/src/pdu.h +++ b/src/pdu.h @@ -18,6 +18,7 @@ extern char *strptime(const char *s, const char *format, struct tm *tm); #define PDU_UD_SIZE (PDU_UD_7BIT_MAX + 1) #define PDU_TIMESTAMP_LEN 14 +#define PDU_CDMA_TIMESTAMP_LEN 12 #define PDU_TIMESTAMP_SIZE (PDU_TIMESTAMP_LEN + 1) #define GMT_OFFSET_LEN 2 #define GMT_OFFSET_IDX (PDU_TIMESTAMP_LEN - GMT_OFFSET_LEN) @@ -45,10 +46,9 @@ enum { }; enum { - PDU_ALPHABET_DEFAULT = 0, - PDU_ALPHABET_EIGHT = 1, - PDU_ALPHABET_UCS2 = 2, - PDU_ALPHABET_RESERVED = 3, + PDU_ALPHABET_DEFAULT = 0, //7-bit + PDU_ALPHABET_CDMA_DEFAULT = 2, //7-bit + PDU_ALPHABET_EIGHT = 4, }; struct pdu_info { @@ -71,8 +71,8 @@ struct pdu_info { union { uint8_t data_coding; struct { - uint8_t msg_class: 2; - uint8_t alphabet: 2; + // uint8_t msg_class: 2; + uint8_t alphabet: 4; uint8_t have_msg_class: 1; uint8_t compressed: 1; uint8_t unused: 2; @@ -113,6 +113,10 @@ int pdu_user_data_read(int fd, struct pdu_info *pdu); #define octet_align(n) ((((n) * 7) + ((-(n) * 7) & 7)) / 8) +#define septet_idx(n) ((n) * 8 / 7) +#define octet_align_cdma(n) septet_idx(n) + + #define STRLEN_CHECK(str, len, ret) \ do { \ if (strnlen(str, len) < len) { \ diff --git a/src/pdu_decode.c b/src/pdu_decode.c index cbc2009..2b9e44c 100644 --- a/src/pdu_decode.c +++ b/src/pdu_decode.c @@ -30,6 +30,7 @@ #include #include "config.h" +#include "global.h" #include "log.h" #include "utils.h" #include "pdu_decode.h" @@ -82,6 +83,31 @@ int pdu_decode_timestamp(const char *pdu_str, struct tm *tm) return PDU_TIMESTAMP_LEN; } + +int pdu_decode_cdma_timestamp(const char *pdu_str, struct tm *tm) +{ + char buf[PDU_CDMA_TIMESTAMP_LEN + 1]; + char *cp; + int off_upper; + int off_lower; + int off; + + STRLEN_CHECK(pdu_str, PDU_CDMA_TIMESTAMP_LEN, -1); + + memset(tm, 0, sizeof(*tm)); + memset(buf, 0, sizeof(buf)); + + strncpy(buf, pdu_str, PDU_CDMA_TIMESTAMP_LEN); + + cp = (char *) strptime(buf, "%y%m%d%H%M%S", tm); + if (!cp || *cp) { + log_error("timestamp could not be converted to tm"); + return -1; + } + + return PDU_CDMA_TIMESTAMP_LEN; +} + int pdu_decode_addr(const char *pdu_str, struct pdu_addr *addr, int smsc) { const char *begin = pdu_str; @@ -141,17 +167,21 @@ done: } #define octet_idx(n) octet_align(n) +#define octet_cdma_idx(n) ((n+1) * 7 / 8) #define decode_septet(buf, n) \ shiftl(bit_maskr(cycledown(n, 8)) & buf[octet_idx(n)], cycleup(n, 8)) | \ shiftr(bit_maskl(cycleup(n, 8), 8) & buf[octet_idx(n - 1)], cycledown(n - 1, 8)) -int pdu_decode_user_data(const char *pdu_str, struct pdu_info *pdu) +#define decode_septet_cdma(buf, n) \ +shiftr(buf[octet_cdma_idx(n)], cycleup(n, 8) + 1) | \ +shiftl(buf[octet_cdma_idx(n) - 1], cycledown(n, 8)) & 0x7F + +int pdu_decode_user_data(const char *pdu_str, struct pdu_info *pdu, int *nr_octets) { const char *begin = pdu_str; int tmp; uint8_t octets[PDU_OCTETS_MAX + 1]; - int nr_octets; int i; STRLEN_CHECK(pdu_str, HEX_BYTE_LEN, -1); @@ -176,7 +206,30 @@ int pdu_decode_user_data(const char *pdu_str, struct pdu_info *pdu) pdu->user_data_len = PDU_UD_7BIT_MAX; } - nr_octets = octet_align(pdu->user_data_len); + *nr_octets = octet_align(pdu->user_data_len); + } else if (pdu->data_coding.general.alphabet == PDU_ALPHABET_CDMA_DEFAULT) { + log_debug("data coding alphabet is cdma default"); + + if (!strcmp(Global.core.model, "LE910-SVG")) { + if (pdu->user_data_len > PDU_UD_7BIT_MAX) { + log_warning("pdu contains invalid user-data-len: 0x%02X", pdu->user_data_len); + pdu->user_data_len = PDU_UD_7BIT_MAX; + } + } + else { + if (pdu->user_data_len > PDU_UD_8BIT_MAX) { + log_warning("pdu contains invalid user-data-len: 0x%02X", pdu->user_data_len); + pdu->user_data_len = PDU_UD_8BIT_MAX; + } + } + + if (!strcmp(Global.core.model, "LE910-SVG")) { + *nr_octets = octet_align(pdu->user_data_len); + } + else { + *nr_octets = pdu->user_data_len; + pdu->user_data_len = octet_align_cdma(pdu->user_data_len); + } } else if (pdu->data_coding.general.alphabet == PDU_ALPHABET_EIGHT) { log_debug("data coding alphabet is eight"); @@ -186,19 +239,19 @@ int pdu_decode_user_data(const char *pdu_str, struct pdu_info *pdu) pdu->user_data_len = PDU_UD_8BIT_MAX; } - nr_octets = pdu->user_data_len; + *nr_octets = pdu->user_data_len; } else { log_debug("data coding alphabet 0x%02X not implemented", pdu->data_coding.general.alphabet); return -1; } - STRLEN_CHECK(pdu_str, nr_octets * 2, -1); + STRLEN_CHECK(pdu_str, *nr_octets * 2, -1); log_debug("user-data-len: 0x%02X", pdu->user_data_len); - log_debug("nr_octets: 0x%02X", nr_octets); + log_debug("nr_octets: 0x%02X", *nr_octets); - for (i = 0; i < nr_octets; i++) { + for (i = 0; i < *nr_octets; i++) { octets[i] = hex_byte_decode(pdu_str); pdu_str += HEX_BYTE_LEN; } @@ -222,6 +275,32 @@ int pdu_decode_user_data(const char *pdu_str, struct pdu_info *pdu) pdu->user_data[i] = decode_septet(octets, i); } pdu->user_data[i] = '\0'; + } else if (pdu->data_coding.general.alphabet == PDU_ALPHABET_CDMA_DEFAULT) { + // Keep UDH for concatenated SMS. + // Otherwise it is impossible to process correctly concatenated SMS. + i = 0; + if (pdu->type.user_data_header) { + for (i = 0; i <= octets[0]; ++i) { + pdu->user_data[i] = octets[i]; + } + i = octets[0] + 1; // Set message text start after UDH. + // Process octets padding for 7-bit encoding. + if (0 != (i * 8) % 7) { + pdu->user_data[i] = 0; // set padding data to 0; + i++; + } + } + for (; i < pdu->user_data_len; i++) { + pdu->user_data[i] = decode_septet_cdma(octets, i); + log_debug("DECODE: i: %d octet: 0x%02X --> data: 0x%02X", i, octets[i], pdu->user_data[i]); + } + //Remove padded byte for data length of 7 characters if not LE910-SVG + if (strcmp(Global.core.model, "LE910-SVG") && pdu->user_data[i-1] == 0) { + log_debug("Removing padded char"); + i--; + pdu->user_data_len--; + } + pdu->user_data[i] = '\0'; } else { for (i = 0; i < pdu->user_data_len; i++) { pdu->user_data[i] = octets[i]; @@ -237,6 +316,7 @@ int pdu_decode(const char *pdu_str, struct pdu_info *pdu) const char *begin = pdu_str; const char *msg_begin; int tmp; + int nr_octets; memset(pdu, 0, sizeof(*pdu)); @@ -341,7 +421,7 @@ int pdu_decode(const char *pdu_str, struct pdu_info *pdu) pdu_str += tmp; } - tmp = pdu_decode_user_data(pdu_str, pdu); + tmp = pdu_decode_user_data(pdu_str, pdu, &nr_octets); DECODE_FAIL(tmp < 0, "user-data", -1); pdu_str += tmp; } @@ -352,3 +432,63 @@ int pdu_decode(const char *pdu_str, struct pdu_info *pdu) return pdu_str - begin; } + + +int pdu_decode_cdma(const char *pdu_str, struct pdu_info *pdu) +{ + const char *begin = pdu_str; + const char *msg_begin; + int tmp; + int nr_octets; + + memset(pdu, 0, sizeof(*pdu)); + + //Destination address + tmp = pdu_decode_addr(pdu_str, &pdu->addr, 1); + DECODE_FAIL(tmp < 0, "addr", -1); + pdu_str += tmp; + + msg_begin = pdu_str; + + STRLEN_CHECK(pdu_str, HEX_BYTE_LEN, -1); + + //Timestamp + tmp = pdu_decode_cdma_timestamp(pdu_str, &pdu->timestamp); + DECODE_FAIL(tmp < 0, "timestamp", -1); + pdu_str += tmp; + + //Teleservice ID + tmp = hex_byte_decode(pdu_str); + DECODE_FAIL(tmp < 0, "tele-id-1", -1); + pdu_str += HEX_BYTE_LEN; + log_debug("tele-id-1: 0x%02X", tmp); + + tmp = hex_byte_decode(pdu_str); + DECODE_FAIL(tmp < 0, "tele-id-2", -1); + pdu_str += HEX_BYTE_LEN; + log_debug("tele-id-2: 0x%02X", tmp); + + //Priority + tmp = hex_byte_decode(pdu_str); + DECODE_FAIL(tmp < 0, "priority", -1); + pdu_str += HEX_BYTE_LEN; + log_debug("priority: 0x%02X", tmp); + + //Data coding scheme + tmp = hex_byte_decode(pdu_str); + DECODE_FAIL(tmp < 0, "data-coding-scheme", -1); + pdu_str += HEX_BYTE_LEN; + pdu->data_coding.data_coding = tmp; + log_debug("data-coding-scheme: 0x%02X", pdu->data_coding.data_coding); + + //User Data + tmp = pdu_decode_user_data(pdu_str, pdu, &nr_octets); + DECODE_FAIL(tmp < 0, "user-data", -1); + pdu_str += tmp; + + pdu->msg_len = 6 + nr_octets; + + log_debug("msg_len: %d", pdu->msg_len); + + return pdu_str - begin; +} diff --git a/src/pdu_decode.h b/src/pdu_decode.h index 004c8ad..c39f232 100644 --- a/src/pdu_decode.h +++ b/src/pdu_decode.h @@ -4,9 +4,11 @@ #include "pdu.h" int pdu_decode_timestamp(const char *pdu_str, struct tm *tm); +int pdu_decode_cdma_timestamp(const char *pdu_str, struct tm *tm); int pdu_decode_addr(const char *pdu_str, struct pdu_addr *addr, int smsc); -int pdu_decode_user_data(const char *pdu_str, struct pdu_info *pdu); +int pdu_decode_user_data(const char *pdu_str, struct pdu_info *pdu, int *nr_octets); int pdu_decode(const char *pdu_str, struct pdu_info *pdu); +int pdu_decode_cdma(const char *pdu_str, struct pdu_info *pdu); #endif /* ~__PDU_DECODE_H */ diff --git a/src/pdu_encode.c b/src/pdu_encode.c index 30cb4a9..1bb7e4d 100644 --- a/src/pdu_encode.c +++ b/src/pdu_encode.c @@ -39,6 +39,8 @@ do { \ } \ } while (0) +#define CEIL(X) ((X-(int)(X)) > 0 ? (int)(X+1) : (int)(X)) + int pdu_encode_timestamp(char *pdu_str, size_t len, struct tm *tm) { int tmp; @@ -75,7 +77,7 @@ int pdu_encode_timestamp(char *pdu_str, size_t len, struct tm *tm) strpad(pdu_str, PDU_TIMESTAMP_LEN, 'F'); nibble_swap(pdu_str, PDU_TIMESTAMP_LEN); - log_debug("%s", pdu_str); + log_debug("encode_timestamp: %s", pdu_str); return PDU_TIMESTAMP_LEN; } @@ -86,7 +88,17 @@ int pdu_encode_addr(char *pdu_str, size_t len, struct pdu_addr *addr, int smsc) int addr_len; int tmp; - addr_len = strlen(addr->addr); + if (isCdmaTypeModel()) { + //CDMA length is # of bytes including address type + //Ex: Type: 80 #: 9132074666 --> 80 19 23 70 64 66 => 6 bytes + addr_len = 1; //1 byte for address type + addr_len += CEIL(strlen(addr->addr) / 2); //Number of hex bytes + } + else { + //GSM length is # of digits in converted number + //Ex: Type: 81 #: 9132074666 --> 1923706466 => 10 digits + addr_len = strlen(addr->addr); + } if (smsc) { if (addr_len) { @@ -136,18 +148,20 @@ done: return pdu_str - begin; } -#define septet_idx(n) ((n) * 8 / 7) + #define encode_octet(buf, n) \ shiftr(bit_maskl(cycledown(n, 7) + 1, 7) & buf[septet_idx(n)], cycleup(n, 7)) | \ shiftl(bit_maskr(cycleup(n, 7) + 1) & buf[septet_idx(n) + 1], cycledown(n, 7) + 1) -int pdu_encode_user_data(char *pdu_str, size_t len, struct pdu_info *pdu) +#define encode_cdma_octet(buf, n) \ +shiftl(buf[septet_idx(n)], cycleup(n, 7) + 1) | shiftr(buf[septet_idx(n) + 1], cycledown(n, 7)) + +int pdu_encode_user_data(char *pdu_str, size_t len, struct pdu_info *pdu, int *nr_octets) { char *begin = pdu_str; int tmp; uint8_t octets[PDU_OCTETS_MAX]; - int nr_octets; int i; if (pdu->data_coding.general.unused) { @@ -157,9 +171,11 @@ int pdu_encode_user_data(char *pdu_str, size_t len, struct pdu_info *pdu) } if (pdu->data_coding.general.alphabet == PDU_ALPHABET_DEFAULT) { - log_debug("data coding alphabet is default"); + log_debug("data coding alphabet is default (7-bit)"); } else if (pdu->data_coding.general.alphabet == PDU_ALPHABET_EIGHT) { log_debug("data coding alphabet is eight"); + } else if (pdu->data_coding.general.alphabet == PDU_ALPHABET_CDMA_DEFAULT) { + log_debug("data coding alphabet is CDMA default (7-bit)"); } else { log_debug("data coding alphabet 0x%02X not implemented", pdu->data_coding.general.alphabet); @@ -172,23 +188,35 @@ int pdu_encode_user_data(char *pdu_str, size_t len, struct pdu_info *pdu) return -1; } - nr_octets = octet_align(pdu->user_data_len); - for (i = 0; i < nr_octets; i++) { + *nr_octets = octet_align(pdu->user_data_len); + for (i = 0; i < *nr_octets; i++) { octets[i] = encode_octet(pdu->user_data, i); } + } else if (pdu->data_coding.general.alphabet == PDU_ALPHABET_CDMA_DEFAULT) { + if (pdu->user_data_len > PDU_UD_7BIT_MAX) { + log_error("string exceeds 7-bit data max length"); + return -1; + } + + //CDMA ENCODING + *nr_octets = octet_align(pdu->user_data_len); + pdu->user_data_len = *nr_octets; + for (i = 0; i < *nr_octets; i++) { + octets[i] = encode_cdma_octet(pdu->user_data, i); + } } else { if (pdu->user_data_len > PDU_UD_8BIT_MAX) { log_error("string exceeds 8-bit data max length"); return -1; } - nr_octets = pdu->user_data_len; + *nr_octets = pdu->user_data_len; for (i = 0; i < pdu->user_data_len; i++) { octets[i] = pdu->user_data[i]; } } - if (len < nr_octets * 2 + HEX_BYTE_LEN + 1) { + if (len < *nr_octets * 2 + HEX_BYTE_LEN + 1) { log_error("buffer is not large enough to hold user-data"); return -1; } @@ -198,15 +226,15 @@ int pdu_encode_user_data(char *pdu_str, size_t len, struct pdu_info *pdu) pdu_str += tmp; len -= tmp; - for (i = 0; i < nr_octets; i++) { + for (i = 0; i < *nr_octets; i++) { hex_byte_encode(pdu_str, len, octets[i]); pdu_str += HEX_BYTE_LEN; } - len -= nr_octets * 2; + len -= *nr_octets * 2; pdu_str[i] = '\0'; log_debug("user-data-len: 0x%02X", pdu->user_data_len); - log_debug("nr_octets: 0x%02X", nr_octets); + log_debug("nr_octets: 0x%02X", *nr_octets); return pdu_str - begin; } @@ -216,6 +244,7 @@ int pdu_encode(char *pdu_str, size_t len, struct pdu_info *pdu) char *begin = pdu_str; char *msg_begin = pdu_str; int tmp; + int nr_octets; tmp = pdu_encode_addr(pdu_str, len, &pdu->smsc_addr, 1); ENCODE_FAIL(tmp < 0, "smsc-addr", -1); @@ -293,7 +322,7 @@ int pdu_encode(char *pdu_str, size_t len, struct pdu_info *pdu) pdu_str += tmp; } - tmp = pdu_encode_user_data(pdu_str, len, pdu); + tmp = pdu_encode_user_data(pdu_str, len, pdu, &nr_octets); ENCODE_FAIL(tmp < 0, "user-data", -1); len -= tmp; pdu_str += tmp; @@ -305,3 +334,71 @@ int pdu_encode(char *pdu_str, size_t len, struct pdu_info *pdu) return pdu_str - begin; } + +int pdu_encode_cdma(char *pdu_str, size_t len, struct pdu_info *pdu) +{ + char *begin = pdu_str; + char *msg_begin = pdu_str; + int tmp; + int nr_octets; + + //Set pdu encoding for CDMA + pdu->data_coding.general.alphabet = PDU_ALPHABET_CDMA_DEFAULT; + + //Destination address + tmp = pdu_encode_addr(pdu_str, len, &pdu->addr, 0); + ENCODE_FAIL(tmp < 0, "addr", -1); + len -= tmp; + pdu_str += tmp; + + //Callback address (Always 00 because it's empty) + tmp = hex_byte_encode(pdu_str, len, 0); + ENCODE_FAIL(tmp != HEX_BYTE_LEN, "callback-addr-len", -1); + pdu_str += tmp; + len -= tmp; + + //Teleservice ID (always 1002) + tmp = hex_byte_encode(pdu_str, len, 16); + ENCODE_FAIL(tmp != HEX_BYTE_LEN, "tele-id-1", -1); + pdu_str += tmp; + len -= tmp; + tmp = hex_byte_encode(pdu_str, len, 2); + ENCODE_FAIL(tmp != HEX_BYTE_LEN, "tele-id-2", -1); + pdu_str += tmp; + len -= tmp; + + //Priority (always 00 for normal) + tmp = hex_byte_encode(pdu_str, len, 0); + ENCODE_FAIL(tmp != HEX_BYTE_LEN, "priority", -1); + pdu_str += tmp; + len -= tmp; + + switch (pdu->type.msg_type) { + case PDU_MTI_REPORT: + log_error("msg_type PDU_MTI_REPORT not supported!"); + return 0; + + case PDU_MTI_DELIVER: + log_debug("msg_type: PDU_MTI_DELIVER"); + case PDU_MTI_SUBMIT: + log_debug("msg_type: PDU_MTI_SUBMIT"); + + //add data coding scheme + tmp = hex_byte_encode(pdu_str, len, pdu->data_coding.data_coding); + ENCODE_FAIL(tmp != HEX_BYTE_LEN, "data-coding-scheme", -1); + len -= tmp; + pdu_str += tmp; + + + tmp = pdu_encode_user_data(pdu_str, len, pdu, &nr_octets); + ENCODE_FAIL(tmp < 0, "user-data", -1); + len -= tmp; + pdu_str += tmp; + } + + // pdu->msg_len = (pdu_str - msg_begin) / 2; + pdu->msg_len = 6 + nr_octets; + log_debug("msg-len: %d", pdu->msg_len); + + return pdu_str - begin; +} diff --git a/src/pdu_encode.h b/src/pdu_encode.h index 246ea3d..8ff79a3 100644 --- a/src/pdu_encode.h +++ b/src/pdu_encode.h @@ -5,8 +5,9 @@ int pdu_encode_timestamp(char *pdu_str, size_t len, struct tm *tm); int pdu_encode_addr(char *pdu_str, size_t len, struct pdu_addr *addr, int smsc); -int pdu_encode_user_data(char *pdu_str, size_t len, struct pdu_info *pdu); +int pdu_encode_user_data(char *pdu_str, size_t len, struct pdu_info *pdu, int *nr_octets); int pdu_encode(char *pdu_str, size_t len, struct pdu_info *pdu); +int pdu_encode_cdma(char *pdu_str, size_t len, struct pdu_info *pdu); #endif /* ~__PDU_ENCODE_H */ diff --git a/src/sms_list.c b/src/sms_list.c index 0ccec15..dcad3fe 100644 --- a/src/sms_list.c +++ b/src/sms_list.c @@ -279,7 +279,14 @@ static int list_info_callback(char *buf, size_t len, void *data) msg = list_entry(list_info->msg_list.prev, typeof(*msg), list); if (msg->len > 0) { - err = pdu_decode(buf, &msg->pdu); + if (isCdmaTypeModel()) { + log_debug("using CDMA pdu decoding"); + err = pdu_decode_cdma(buf, &msg->pdu); + } + else { + log_debug("using GSM pdu decoding"); + err = pdu_decode(buf, &msg->pdu); + } if (err < 0) { log_warning("pdu decode failed: %d", err); } diff --git a/src/sms_main.c b/src/sms_main.c index 1bf74fa..e91aaba 100644 --- a/src/sms_main.c +++ b/src/sms_main.c @@ -59,6 +59,7 @@ static int global_init(void) Global.core.msg_store_send = strdup("SM"); Global.core.msg_store_new = strdup("SM"); Global.core.pb_store = strdup("SM"); + Global.core.model = strdup("UNKNOWNMODEL"); Global.core.editor = strdup("vi"); Global.core.edit_file = strdup("${HOME}/.smsmsg"); diff --git a/src/sms_send.c b/src/sms_send.c index 8ed7ebb..3933786 100644 --- a/src/sms_send.c +++ b/src/sms_send.c @@ -224,8 +224,25 @@ static int do_send(int fd, struct send_options *options, int argc, char **argv) printf("preparing for send...\n"); } + //LE910-SVG SMS SEND WORKAROUND + if (!strcmp(Global.core.model, "LE910-SVG")) { + log_info("setting text mode for LE910-SVG"); + tmp = atcmd_plus_cmgf_write(fd, SMS_TEXT_MODE); + if (tmp < 0) { + log_error("failed to set text mode for sending with LE910-SVG"); + return false; + } + } + if (options->cmgw_first) { - tmp = pdu_encode(buf, sizeof(buf), &pdu); + if (isCdmaTypeModel()) { + log_debug("using CDMA pdu encoding for cmgw"); + tmp = pdu_encode_cdma(buf, sizeof(buf), &pdu); + } + else { + log_debug("using GSM pdu encoding for cmgw"); + tmp = pdu_encode(buf, sizeof(buf), &pdu); + } if (tmp < 0) { log_error("pdu encode failed"); return false; @@ -235,7 +252,13 @@ static int do_send(int fd, struct send_options *options, int argc, char **argv) printf("writing message to memory\n"); } - mem_index = atcmd_plus_cmgw_write(fd, buf, pdu.msg_len); + //LE910-SVG SMS SEND WORKAROUND + if (!strcmp(Global.core.model, "LE910-SVG")) { + mem_index = atcmd_plus_cmgw_write_text(fd, NULL, SMS_ADDR_UNSPEC, NULL, pdu.user_data, pdu.user_data_len); + } + else { + mem_index = atcmd_plus_cmgw_write(fd, buf, pdu.msg_len); + } if (mem_index < 0) { log_error("write message to memory failed"); return false; @@ -275,14 +298,27 @@ static int do_send(int fd, struct send_options *options, int argc, char **argv) continue; } } else { - tmp = pdu_encode(buf, sizeof(buf), &pdu); + if (isCdmaTypeModel()) { + log_debug("using CDMA pdu encoding for cmgs"); + tmp = pdu_encode_cdma(buf, sizeof(buf), &pdu); + } + else { + log_debug("using GSM pdu encoding for cmgs"); + tmp = pdu_encode(buf, sizeof(buf), &pdu); + } if (tmp < 0) { printf("sending message to %s failed\n", argv[i]); failed++; continue; } - tmp = atcmd_plus_cmgs_write(fd, buf, pdu.msg_len); + //LE910-SVG SMS SEND WORKAROUND + if (!strcmp(Global.core.model, "LE910-SVG")) { + tmp = atcmd_plus_cmgs_write_text(fd, pdu.addr.addr, pdu.user_data, pdu.user_data_len); + } + else { + tmp = atcmd_plus_cmgs_write(fd, buf, pdu.msg_len); + } if (tmp < 0) { printf("sending message to %s failed\n", argv[i]); failed++; -- cgit v1.2.3