From a3ef04c8d98df1e65432094a33c2df25b3543f74 Mon Sep 17 00:00:00 2001 From: Jared Baumann Date: Mon, 20 Mar 2023 16:10:27 +0000 Subject: [PATCH] drivers: modem: Added SMS receive callback Improved SMS interface, and added funcitonality to support an SMS receive callback. Signed-off-by: Jared Baumann --- .gitattributes | 3 + drivers/modem/CMakeLists.txt | 1 + drivers/modem/Kconfig | 19 +- drivers/modem/Kconfig.murata-1sc | 1 + drivers/modem/Kconfig.sms | 43 + drivers/modem/modem_context.h | 9 +- drivers/modem/modem_shell.c | 15 +- drivers/modem/modem_sms.c | 117 ++ drivers/modem/modem_sms.h | 54 +- drivers/modem/murata-1sc.c | 1807 +++++++++++++++------------- include/zephyr/drivers/modem/sms.h | 162 +++ 11 files changed, 1339 insertions(+), 892 deletions(-) create mode 100644 drivers/modem/Kconfig.sms create mode 100644 drivers/modem/modem_sms.c create mode 100644 include/zephyr/drivers/modem/sms.h diff --git a/.gitattributes b/.gitattributes index 4c8467c608c..f85cdfb52be 100644 --- a/.gitattributes +++ b/.gitattributes @@ -109,8 +109,10 @@ drivers/i2c/i2c_gecko.c merge=ours drivers/modem/CMakeLists.txt merge=ours drivers/modem/Kconfig merge=ours drivers/modem/Kconfig.murata-1sc merge=ours +drivers/modem/Kconfig.sms merge=ours drivers/modem/modem_context.h merge=ours drivers/modem/modem_shell.c merge=ours +drivers/modem/modem_sms.c merge=ours drivers/modem/modem_sms.h merge=ours drivers/modem/modem_socket.c merge=ours drivers/modem/modem_socket.h merge=ours @@ -175,6 +177,7 @@ include/zephyr/drivers/bluetooth/rs9116w.h merge=ours include/zephyr/drivers/fuel_gauge.h merge=ours include/zephyr/drivers/fuel_gauge/act81461.h merge=ours include/zephyr/drivers/modem/murata-1sc.h merge=ours +include/zephyr/drivers/modem/sms.h merge=ours include/zephyr/drivers/rtc/gecko_rtcc.h merge=ours include/zephyr/drivers/sensor.h merge=ours include/zephyr/drivers/sensor/cxd5605.h merge=ours diff --git a/drivers/modem/CMakeLists.txt b/drivers/modem/CMakeLists.txt index c5c9a13ac95..2c44c726674 100644 --- a/drivers/modem/CMakeLists.txt +++ b/drivers/modem/CMakeLists.txt @@ -11,6 +11,7 @@ zephyr_library_sources_ifdef(CONFIG_MODEM_IFACE_UART_INTERRUPT modem_iface_uart_ zephyr_library_sources_ifdef(CONFIG_MODEM_IFACE_UART_ASYNC modem_iface_uart_async.c) zephyr_library_sources_ifdef(CONFIG_MODEM_CMD_HANDLER modem_cmd_handler.c) zephyr_library_sources_ifdef(CONFIG_MODEM_SOCKET modem_socket.c) +zephyr_library_sources_ifdef(CONFIG_MODEM_SMS modem_sms.c) if(CONFIG_MODEM_UBLOX_SARA) zephyr_library_include_directories(${ZEPHYR_BASE}/subsys/net/ip) diff --git a/drivers/modem/Kconfig b/drivers/modem/Kconfig index cafa764ad75..51f8bdc8fb0 100644 --- a/drivers/modem/Kconfig +++ b/drivers/modem/Kconfig @@ -189,28 +189,11 @@ config MODEM_CELL_INFO help Query for numerical operator id, location area code and cell id. -config MODEM_SMS_IN_MSG_MAX_LEN - int "Maximum length of inbound SMS messages" - default 160 - help - This is the maximum length of inbound (received) SMS messages. - Inbound SMS messages longer than this will be truncated. - The absolute maximum value may be network- and/or modem-dependent, - but a smaller value may be preferred in order to conserve memory. - -config MODEM_SMS_OUT_MSG_MAX_LEN - int "Maximum length of outbound SMS messages" - default MODEM_SMS_IN_MSG_MAX_LEN - help - This is the maximum length of outbound SMS messages (being sent). - SMS message send requests longer than this are not possible. - The absolute maximum value may be network- and/or modem-dependent, - but a smaller value may be preferred in order to conserve memory. - source "drivers/modem/Kconfig.ublox-sara-r4" source "drivers/modem/Kconfig.quectel-bg9x" source "drivers/modem/Kconfig.wncm14a2a" source "drivers/modem/Kconfig.gsm" +source "drivers/modem/Kconfig.sms" source "drivers/modem/Kconfig.hl7800" source "drivers/modem/Kconfig.simcom-sim7080" diff --git a/drivers/modem/Kconfig.murata-1sc b/drivers/modem/Kconfig.murata-1sc index 55421b291a1..975b9712cf0 100644 --- a/drivers/modem/Kconfig.murata-1sc +++ b/drivers/modem/Kconfig.murata-1sc @@ -9,6 +9,7 @@ menuconfig MODEM_MURATA_1SC select MODEM_CMD_HANDLER select MODEM_IFACE_UART select MODEM_SOCKET + select NET_SOCKETS select NET_SOCKETS_OFFLOAD select BASE64 imply GPIO diff --git a/drivers/modem/Kconfig.sms b/drivers/modem/Kconfig.sms new file mode 100644 index 00000000000..ea382e60790 --- /dev/null +++ b/drivers/modem/Kconfig.sms @@ -0,0 +1,43 @@ +# Modem SMS options + +# Copyright (c) 2023 T-Moblie +# SPDX-License-Identifier: Apache-2.0 + +menuconfig MODEM_SMS + bool "Modem SMS handler" + default y + depends on MODEM_CONTEXT + help + Choose this setting to enable SMS functionality for + modems. + +if MODEM_SMS + +config MODEM_SMS_IN_MSG_MAX_LEN + int "Maximum length of inbound SMS messages" + default 160 + help + This is the maximum length of inbound (received) SMS messages. + Inbound SMS messages longer than this will be truncated. + The absolute maximum value may be network- and/or modem-dependent, + but a smaller value may be preferred in order to conserve memory. + +config MODEM_SMS_OUT_MSG_MAX_LEN + int "Maximum length of outbound SMS messages" + default MODEM_SMS_IN_MSG_MAX_LEN + help + This is the maximum length of outbound SMS messages (being sent). + SMS message send requests longer than this are not possible. + The absolute maximum value may be network- and/or modem-dependent, + but a smaller value may be preferred in order to conserve memory. + +config MODEM_SMS_CALLBACK + bool "Use SMS Receive callbacks" + default n + help + This option allows a callback to be registered for SMS receive + events. SMS messages reported via the callback will not be + buffered, and therefore client code must handle buffering if + required. + +endif # MODEM_SMS diff --git a/drivers/modem/modem_context.h b/drivers/modem/modem_context.h index 05d147c8524..7e380255593 100644 --- a/drivers/modem/modem_context.h +++ b/drivers/modem/modem_context.h @@ -20,7 +20,8 @@ #include #include #include -#include "modem_sms.h" + +#include #ifdef __cplusplus extern "C" { @@ -74,9 +75,15 @@ struct modem_context { /* command handler config */ struct modem_cmd_handler cmd_handler; + /* modem device pointer */ + const struct device *dev; + +#if defined(CONFIG_MODEM_SMS) /* SMS functions */ send_sms_func_t send_sms; recv_sms_func_t recv_sms; + recv_sms_cb_en_func_t recv_sms_cb_en; +#endif /* driver data */ void *driver_data; diff --git a/drivers/modem/modem_shell.c b/drivers/modem/modem_shell.c index 417034109c4..d7429c2f203 100644 --- a/drivers/modem/modem_shell.c +++ b/drivers/modem/modem_shell.c @@ -13,8 +13,6 @@ #define LOG_MODULE_NAME modem_shell -#include "modem_sms.h" - #include #include #include @@ -22,6 +20,10 @@ #include #include +#if defined(CONFIG_MODEM_SMS) +#include +#endif /* CONFIG_MODEM_SMS */ + #include struct modem_shell_user_data { @@ -241,6 +243,7 @@ static int cmd_modem_info(const struct shell *shell, size_t argc, char *argv[]) return 0; } +#if defined(CONFIG_MODEM_SMS) static int cmd_modem_sms_send(const struct shell *shell, size_t argc, char *argv[]) { @@ -278,7 +281,7 @@ static int cmd_modem_sms_send(const struct shell *shell, size_t argc, snprintk(sms.phone, sizeof(sms.phone), "%s", argv[2]); snprintk(sms.msg, sizeof(sms.msg), "%s", argv[3]); - ret = ms_ctx->send_sms(ms_ctx, &sms); + ret = ms_ctx->send_sms(&sms); if (ret == 0) { shell_fprintf(shell, SHELL_NORMAL, "SMS msg was sent\n"); @@ -321,7 +324,7 @@ static int cmd_modem_sms_recv(const struct shell *shell, size_t argc, } sms.timeout = K_SECONDS(wait); - ret = ms_ctx->recv_sms(ms_ctx, &sms); + ret = ms_ctx->recv_sms(&sms); if (ret < 0) { shell_fprintf(shell, SHELL_ERROR, "recv_sms returned error %d, errno:%d\n", @@ -350,6 +353,8 @@ SHELL_STATIC_SUBCMD_SET_CREATE(modem_cmd_sms, cmd_modem_sms_recv), SHELL_SUBCMD_SET_END); +#endif /* CONFIG_MODEM_SMS */ + SHELL_STATIC_SUBCMD_SET_CREATE( sub_modem, SHELL_CMD(info, NULL, "Show information for a modem", cmd_modem_info), @@ -358,8 +363,10 @@ SHELL_STATIC_SUBCMD_SET_CREATE( "Send an AT to a registered modem " "receiver", cmd_modem_send), +#if defined(CONFIG_MODEM_SMS) SHELL_CMD(sms, &modem_cmd_sms, "Send or receive SMS message via modem", NULL), +#endif /* CONFIG_MODEM_SMS */ SHELL_SUBCMD_SET_END /* Array terminated. */ ); diff --git a/drivers/modem/modem_sms.c b/drivers/modem/modem_sms.c new file mode 100644 index 00000000000..2ec87b3d2af --- /dev/null +++ b/drivers/modem/modem_sms.c @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2023 T-Mobile USA, Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include "modem_sms.h" +#include "modem_context.h" + +static sys_slist_t sms_recv_cbs = SYS_SLIST_STATIC_INIT(&sms_recv_cbs); + +void notify_sms_recv(const struct device *dev, struct sms_in *sms, int csms_ref, int csms_idx, + int csms_tot) +{ + struct sms_recv_cb *cb, *next; + + SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&sms_recv_cbs, cb, next, node) { + if (cb->recv) { + cb->recv(dev, sms, csms_ref, csms_idx, csms_tot); + } + } +} + +static struct modem_context *modem_context_from_modem_dev(const struct device *dev) +{ + struct modem_context *mctx; + + for (int i = 0; i < CONFIG_MODEM_CONTEXT_MAX_NUM; i++) { + mctx = modem_context_from_id(i); + if (mctx && mctx->dev == dev) { + return mctx; + } + } + return 0; +} + +int sms_msg_send(const struct device *dev, const struct sms_out *sms) +{ + struct modem_context *mctx; + + mctx = modem_context_from_modem_dev(dev); + + if (!mctx) { + return -ENODEV; + } + + if (!mctx->send_sms) { + return -ENOSYS; + } + + return mctx->send_sms(sms); +} + +int sms_msg_recv(const struct device *dev, struct sms_in *sms) +{ + struct modem_context *mctx; + + mctx = modem_context_from_modem_dev(dev); + + if (!mctx) { + return -ENODEV; + } + + if (!mctx->recv_sms) { + return -ENOSYS; + } + + return mctx->recv_sms(sms); +} + +#if defined(CONFIG_MODEM_SMS_CALLBACK) + +int sms_recv_cb_en(const struct device *dev, bool enable) +{ + struct modem_context *mctx; + + mctx = modem_context_from_modem_dev(dev); + + if (!mctx) { + return -ENODEV; + } + + if (!mctx->recv_sms_cb_en) { + return -ENOSYS; + } + + return mctx->recv_sms_cb_en(enable); +} + +int sms_recv_cb_register(struct sms_recv_cb *cb) +{ + if (cb == NULL) { + return -EINVAL; + } + + sys_slist_append(&sms_recv_cbs, &cb->node); + + return 0; +} + +int sms_recv_cb_unregister(struct sms_recv_cb *cb) +{ + if (cb == NULL) { + return -EINVAL; + } + + if (!sys_slist_find_and_remove(&sms_recv_cbs, &cb->node)) { + return -EALREADY; + } + + return 0; +} + +#endif /* CONFIG_MODEM_SMS_CALLBACK */ diff --git a/drivers/modem/modem_sms.h b/drivers/modem/modem_sms.h index a9a0cb6c2c4..447831ae07e 100644 --- a/drivers/modem/modem_sms.h +++ b/drivers/modem/modem_sms.h @@ -1,47 +1,23 @@ -/** @file - * @brief Modem SMS for SMS common structure. - * - * Modem SMS handling for modem driver. - */ - /* - * Copyright (c) 2022 T-Mobile USA, Inc. + * Copyright (c) 2023 T-Mobile USA, Inc. * * SPDX-License-Identifier: Apache-2.0 */ -#ifndef ZEPHYR_INCLUDE_DRIVERS_MODEM_MODEM_SMS_H_ -#define ZEPHYR_INCLUDE_DRIVERS_MODEM_MODEM_SMS_H_ +#ifndef ZEPHYR_DRIVERS_MODEM_MODEM_SMS_H_ +#define ZEPHYR_DRIVERS_MODEM_MODEM_SMS_H_ -#include - -#define SMS_PHONE_MAX_LEN 16 -#define SMS_TIME_MAX_LEN 26 - -/* - * All fields in sms_out and sms_in are NULL terminated +/** + * @brief Notify all registered callbacks of a received SMS message + * + * @param dev Device pointer of modem which received the SMS message + * @param sms Received SMS message + * @param csms_ref CSMS Reference number (if available, value is set to -1 if not) + * @param csms_idx CSMS Index number (if available, value is set to 0 if not) + * @param csms_tot CSMS Total segment count (if available, value is set to 1 if not) + * */ +void notify_sms_recv(const struct device *dev, struct sms_in *sms, int csms_ref, int csms_idx, + int csms_tot); -struct sms_out { - char phone[SMS_PHONE_MAX_LEN]; - char msg[CONFIG_MODEM_SMS_OUT_MSG_MAX_LEN + 1]; -}; - -struct sms_in { - char phone[SMS_PHONE_MAX_LEN]; - char time[SMS_TIME_MAX_LEN]; - char msg[CONFIG_MODEM_SMS_IN_MSG_MAX_LEN + 1]; - uint8_t csms_ref; - uint8_t csms_idx; - k_timeout_t timeout; -}; - -typedef int (*send_sms_func_t)(void *obj, const struct sms_out *sms); -typedef int (*recv_sms_func_t)(void *obj, struct sms_in *sms); - -enum io_ctl { - SMS_SEND, - SMS_RECV, -}; - -#endif /* ZEPHYR_INCLUDE_DRIVERS_MODEM_MODEM_SMS_H_ */ +#endif /* ZEPHYR_DRIVERS_MODEM_MODEM_SMS_H_ */ diff --git a/drivers/modem/murata-1sc.c b/drivers/modem/murata-1sc.c index 2281994e4ad..00e209625bd 100644 --- a/drivers/modem/murata-1sc.c +++ b/drivers/modem/murata-1sc.c @@ -14,8 +14,8 @@ LOG_MODULE_REGISTER(modem_murata_1sc, CONFIG_MODEM_LOG_LEVEL); #include "modem_context.h" #include "modem_iface_uart.h" #include "modem_receiver.h" -#include "modem_sms.h" #include "modem_socket.h" +#include "modem_sms.h" #include #include @@ -27,6 +27,7 @@ LOG_MODULE_REGISTER(modem_murata_1sc, CONFIG_MODEM_LOG_LEVEL); #include #include #include +#include #include #if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) #include "tls_internal.h" @@ -71,6 +72,50 @@ const struct mdmdata_cmd_t cmd_pool[] = {{"APN", apn_e}, {"WAKE", wake_e}, {}}; +enum sms_tp_flags { + TP_FLAG_MMS = BIT(2), + TP_FLAG_RP = BIT(7), + TP_FLAG_UDHI = BIT(6), + TP_FLAG_SRI = BIT(5) +}; + +enum sms_type_of_number { + SMS_TON_UNKNOWN = 0, + SMS_TON_INTERNATIONAL, + SMS_TON_NATIONAL, + SMS_TON_NETWORK_SPECIFIC, + SMS_TON_SUBSCRIBER, + SMS_TON_ALPHANUMERIC, + SMS_TON_ABBREVIATED, + SMS_TON_RESERVED, +}; + +enum sms_alphabet { + SMS_ALPHABET_GSM7 = 0, + SMS_ALPHABET_GSM8, + SMS_ALPHABET_UCS2, +}; + +/* Structure for storing information about a SMS-DELIVER PDU */ +struct deliver_pdu_data_s { + uint8_t smsc_len; /* Length of SMSC segment */ + char *smsc_start; /* SMSC segment */ + uint8_t tp_flags; /* Flags */ + uint8_t oa_len; /* Originator address length */ + char *oa; /* Originator address */ + uint8_t alphabet; /* Alphabet used */ + char *scts; /* Timestamp */ + uint8_t udl; /* User data length */ + uint8_t udhl; /* User data header length */ + char *ud; /* User data */ +}; + +struct csms_data_s { + uint8_t csms_ref; + uint8_t csms_idx; + uint8_t csms_tot; +}; + #if defined(CONFIG_NET_SOCKETS_SOCKOPT_TLS) #define CERTCMD_WRITE_SIZE 32 + MAX_FILENAME_LEN /* assume filename maxlen = 32 */ @@ -227,7 +272,6 @@ struct murata_1sc_data { uint8_t sms_indices[16]; uint8_t sms_csms_indices[16]; struct sms_in *sms; - recv_sms_func_t recv_sms; }; /* Modem pins - Wake Host, Wake Modem, Reset, and Reset Done */ @@ -516,286 +560,805 @@ static int on_cmd_sockread_common(int socket_fd, struct modem_cmd_handler_data * return ret; } +#if defined(CONFIG_MODEM_SMS) + /** - * @brief Handler for receiving unsolicited SMS messages + * @brief Logic to unpack septets + * + * @param frag Packed septet framents + * @param frag_len Length of packed fragments + * @param out Output: Septets, unpacked into an octet (byte) array */ -MODEM_CMD_DEFINE(on_cmd_unsol_sms) +void gsmunpack_frag(uint8_t *frag, int frag_len, uint8_t *out) { - LOG_DBG("SMS Received"); - k_sem_give(&mdata.sem_rcv_sms); - - return 0; + switch (frag_len) { + case 7: + out[7] = frag[6] >> 1; + out[6] = (frag[5] >> 2) | ((frag[6] & 0x01) << 6); + case 6: + out[5] = (frag[4] >> 3) | ((frag[5] & 0x03) << 5); + case 5: + out[4] = (frag[3] >> 4) | ((frag[4] & 0x07) << 4); + case 4: + out[3] = (frag[2] >> 5) | ((frag[3] & 0x0F) << 3); + case 3: + out[2] = (frag[1] >> 6) | ((frag[2] & 0x1F) << 2); + case 2: + out[1] = (frag[0] >> 7) | ((frag[1] & 0x3F) << 1); + case 1: + out[0] = frag[0] & 0x7F; + } } /** - * @brief Handler for unsolicited events ( SOCKETEV) + * @brief Logic for converting GSM 7-bit characters into ASCII + * + * @param gsm GSM septet + * @param escaped Flag for if the previous character was an escape character + * @return char ASCII equivalent character */ -MODEM_CMD_DEFINE(on_cmd_unsol_SEV) +char gsm2ascii(uint8_t gsm, bool escaped) { - struct modem_socket *sock; - int sock_fd; - int evt_id; - - LOG_DBG("got unsolicit socketev, evt: %s, sockfd: %s", argv[0], argv[1]); - evt_id = ATOI(argv[0], 0, "event_id"); - sock_fd = ATOI(argv[1], 0, "sock_fd"); - /* TODO - handle optional connected fd */ - sock = modem_socket_from_fd(&mdata.socket_config, sock_fd); - if (!sock) { - return 0; + if (escaped) { + switch (gsm) { + case 0x0A: + return '\f'; + case 0x14: + return '^'; + case 0x28: + return '{'; + case 0x29: + return '}'; + case 0x2F: + return '\\'; + case 0x3c: + return '['; + case 0x3d: + return '~'; + case 0x3e: + return ']'; + case 0x40: + return '|'; + default: + return '\0'; + } } - - /* Data ready indication. */ - switch (evt_id) { - case 0: /* in execution */ - break; - case 1: /* Rx Rdy */ - LOG_DBG("Data Receive Indication for socket: %d", sock_fd); - - modem_socket_packet_size_update(&mdata.socket_config, sock, 1); - modem_socket_data_ready(&mdata.socket_config, sock); - - break; - /* TODO: need to save the indication that the socket has been - * terminated remotely and treat properly in send and recv - * functions - */ - case 2: /* socket deactivated */ - case 3: /* socket terminated */ - LOG_WRN("Remote peer closed for socket: %d", sock_fd); - break; - case 4: /* socket accepted */ - break; - case 6: /* socket activation done */ - break; + if ((gsm >= 'a' && gsm <= 'z') || (gsm >= 'A' && gsm <= 'Z') || (gsm >> 4) == 3 || + (((gsm >> 4) == 2) && gsm != 0x24)) { + return gsm; + } + switch (gsm) { + case 0x00: + return '@'; + case 0x02: + return '$'; + case '\n': + case '\r': + return gsm; + case 0x11: + return '_'; default: - break; + return '\0'; } - - return 0; -} - -/** - * @brief Handler for manufacturer - */ -MODEM_CMD_DEFINE(on_cmd_get_manufacturer) -{ - modem_cmd_handler_set_error(data, 0); - - size_t out_len = net_buf_linearize( - mdata.mdm_manufacturer, sizeof(mdata.mdm_manufacturer) - 1, data->rx_buf, 0, len); - mdata.mdm_manufacturer[out_len] = '\0'; - LOG_DBG("Manufacturer: %s", mdata.mdm_manufacturer); - return 0; } /** - * @brief Handler for model + * @brief Combined logic for converting a septet payload into an ASCII string + * + * @param in Binary encoding of the septet payload/SMS User Data + * @param udl Length of septet payload/SMS User Data + * @param out Buffer for output + * @param outlen Length of output buffer + * @param skip Number of septets to skip + * @return int 0 on success */ -MODEM_CMD_DEFINE(on_cmd_get_model) +int gsm7_decode(char *in, int udl, char *out, int outlen, int skip) { - size_t out_len = net_buf_linearize(mdata.mdm_model, sizeof(mdata.mdm_model) - 1, - data->rx_buf, 0, len); - mdata.mdm_model[out_len] = '\0'; - - LOG_DBG("Model: %s", mdata.mdm_model); - return 0; -} + uint8_t packed[7], unpacked[8]; + uint8_t processed = 0, escaped_cnt = 0; + uint8_t udl_octets = ((udl * 7 + 7) / 8); + int skip_drp = skip; + char *out_orig = out; + bool escaped = false; -/** - * @brief Handler for IMEI - */ -MODEM_CMD_DEFINE(on_cmd_get_imei) -{ - size_t out_len = - net_buf_linearize(mdata.mdm_imei, sizeof(mdata.mdm_imei) - 1, data->rx_buf, 0, len); - mdata.mdm_imei[out_len] = '\0'; + for (int i = 0; i < udl_octets; i += 7) { + memset(packed, 0, 7); + hex_str_to_data(&in[i * 2], packed, MIN(7, udl_octets - processed)); + gsmunpack_frag(packed, MIN(7, udl_octets - processed), unpacked); + processed += 7; + for (int j = 0; j < 8; j++) { + if (skip) { + skip--; + } else if (unpacked[j] == 0x1b) { + escaped = true; + escaped_cnt++; + } else { + if (out > (out_orig + outlen - 1)) { + return 1; + } + char chr = gsm2ascii(unpacked[j], escaped); - LOG_DBG("IMEI: %s", mdata.mdm_imei); + if (chr) { + *out = chr; + out++; + } + escaped = false; + } + } + } + out_orig[MIN((udl - escaped_cnt - skip_drp), outlen)] = '\0'; return 0; } -#if defined(CONFIG_MODEM_SIM_NUMBERS) /** - * @brief Handler for IMSI + * @brief Function for converting a UTF-16LE encoded character into UTF-8 + * + * @param in Pointer to UTF-16 character + * @param out Pointer to UTF-8 output buffer + * @param out_len Length of output buffer + * @param next_char Pointer to new position in output buffer + * @return int 0 on success else negative errno code */ -MODEM_CMD_DEFINE(on_cmd_get_imsi) +int utf16le_to_utf8(uint16_t *in, uint8_t *out, size_t out_len, uint16_t **next_char) { - size_t out_len = - net_buf_linearize(mdata.mdm_imsi, sizeof(mdata.mdm_imsi) - 1, data->rx_buf, 0, len); - mdata.mdm_imsi[out_len] = '\0'; + uint32_t codepoint; - LOG_DBG("IMSI: %s", mdata.mdm_imsi); - return 0; -} + if (!out || !in || !out_len || !*in) { + return -EINVAL; + } + if (in[1] != 0 && ((sys_le16_to_cpu(in[0]) & 0xFC00) == 0xD800) && + (sys_le16_to_cpu(in[1]) & 0xFC00) == 0xDC00) { + codepoint = (sys_le16_to_cpu(in[0]) - 0xD800) << 10; + codepoint += sys_le16_to_cpu(in[1]) - 0xDC00; + codepoint += 0x10000; + } else { + codepoint = in[0]; + } -/** - * @brief Handler for ICCID - */ -MODEM_CMD_DEFINE(on_cmd_get_iccid) -{ - size_t out_len = net_buf_linearize(mdata.mdm_iccid, sizeof(mdata.mdm_iccid) - 1, - data->rx_buf, 0, len); - mdata.mdm_iccid[out_len] = '\0'; - - LOG_DBG("ICCID: %s", mdata.mdm_iccid); + if (codepoint <= 0x7F) { + *out = (uint8_t)codepoint; + out++; + } else if (codepoint <= 0x7FF) { + if (out_len < 2) { + return -EIO; + } + *out = 0xC0 | (codepoint >> 6); + out++; + *out = 0x80 | (codepoint & 0x3F); + out++; + } else if (codepoint <= 0xFFFF) { + if (out_len < 3) { + return -EIO; + } + *out = 0xE0 | (codepoint >> 12); + out++; + *out = 0x80 | ((codepoint >> 6) & 0x3F); + out++; + *out = 0x80 | (codepoint & 0x3F); + out++; + } else { + if (out_len < 4) { + return -EIO; + } + *out = 0xF0 | (codepoint >> 18); + out++; + *out = 0x80 | ((codepoint >> 12) & 0x3F); + out++; + *out = 0x80 | ((codepoint >> 6) & 0x3F); + out++; + *out = 0x80 | (codepoint & 0x3F); + out++; + } + if (next_char) { + *next_char = codepoint > 65535 ? &in[2] : &in[1]; + } return 0; } -#endif /* defined(CONFIG_MODEM_SIM_NUMBERS) */ /** - * @brief Handler for BAND info + * @brief Function for parsing the PDU structure for a SMS Deliver PDU + * + * @param buf Raw PDU buffer + * @param pdu_data Output structure */ -MODEM_CMD_DEFINE(on_cmd_get_bands) +void deliver_pdu_parse(char *buf, struct deliver_pdu_data_s *pdu_data) { -#define MAX_BANDS_STR_SZ 64 - char bandstr[MAX_BANDS_STR_SZ]; - size_t out_len = net_buf_linearize(bandstr, sizeof(bandstr) - 1, data->rx_buf, 0, len); - bandstr[out_len] = '\0'; - - LOG_DBG("BANDS - %s", bandstr); - - return 0; + pdu_data->smsc_len = hex_byte_to_data(buf); + pdu_data->smsc_start = pdu_data->smsc_len ? buf + 2 : NULL; + buf += 2 + pdu_data->smsc_len * 2; + pdu_data->tp_flags = hex_byte_to_data(buf) & ~0x3; + buf += 2; + pdu_data->oa_len = hex_byte_to_data(buf); + buf += 2; + pdu_data->oa = buf; + if ((hex_byte_to_data(buf) & 0x70) != 0x50) { + buf += 2 + pdu_data->oa_len; + buf += (pdu_data->oa_len % 2); + } else { + buf += 2 + (((pdu_data->oa_len + 1) * 7 / 8) * 2); + } + buf += 2; /* Skip TP-PID */ + pdu_data->alphabet = (hex_byte_to_data(buf) & 0xC) >> 2; + buf += 2; + pdu_data->scts = buf; + buf += 14; + pdu_data->udl = hex_byte_to_data(buf); + buf += 2; + pdu_data->ud = buf; + pdu_data->udhl = (pdu_data->tp_flags & TP_FLAG_UDHI) ? hex_byte_to_data(buf) : 0; } -/** - * @brief Handler for GETACFG=modem_apps.Mode.AutoConnectMode - */ -static bool needto_set_autoconn_to_true; -MODEM_CMD_DEFINE(on_cmd_get_acfg) +int pdu_msg_extract(char *pdu_buffer, struct csms_data_s *csms_data) { -#define MAX_AUTOCONN_STR_SZ 16 - char autoconnmode_str[MAX_AUTOCONN_STR_SZ]; - size_t out_len = net_buf_linearize(autoconnmode_str, sizeof(autoconnmode_str) - 1, - data->rx_buf, 0, len); - autoconnmode_str[out_len] = '\0'; + int ret; + struct sms_in *sms; + bool first_msg = false; + char *out_buf; + size_t out_buf_avail; + struct csms_data_s csms_data_dummy; - if (strncmp(autoconnmode_str, "false", strlen("false")) == 0) { - needto_set_autoconn_to_true = true; - } else { - needto_set_autoconn_to_true = false; - LOG_DBG("Auto Conn Mode: %s", autoconnmode_str); + if (!csms_data) { + csms_data = &csms_data_dummy; } - return 0; -} -/** - * @brief Handler for socket count info - */ -static bool needto_set_sockcount; -MODEM_CMD_DEFINE(on_cmd_get_sockcount) -{ - char sockcount_str[16]; - size_t out_len = - net_buf_linearize(sockcount_str, sizeof(sockcount_str) - 1, data->rx_buf, 0, len); - sockcount_str[out_len] = '\0'; + csms_data->csms_ref = -1; + csms_data->csms_tot = 1; + csms_data->csms_idx = 1; - if (strtol(sockcount_str, NULL, 10) != MDM_MAX_SOCKETS) { - needto_set_sockcount = true; - } else { - needto_set_sockcount = false; + /* No buffer specified. */ + if (!mdata.sms) { + return 0; } - return 0; -} -#ifdef VERIFY_INIT_MODEM_STATE -/** - * @brief Handler for CFUN - */ -MODEM_CMD_DEFINE(on_cmd_get_cfun) -{ -#define MAX_CFUN_STR_SZ 16 - char cfun_resp_str[MAX_CFUN_STR_SZ]; - size_t out_len = - net_buf_linearize(cfun_resp_str, sizeof(cfun_resp_str) - 1, data->rx_buf, 0, len); - cfun_resp_str[out_len] = '\0'; + sms = mdata.sms; + out_buf = sms->msg; + out_buf += strlen(sms->msg); - LOG_DBG("CFUN: %s", cfun_resp_str); - return 0; -} + out_buf_avail = sizeof(sms->msg) - (strlen(sms->msg) + 1); + struct deliver_pdu_data_s pdu_data; -/** - * @brief Handler for CEREG - */ -MODEM_CMD_DEFINE(on_cmd_get_cereg) -{ -#define MAX_CEREG_STR_SZ 16 - char cereg_resp_str[MAX_CEREG_STR_SZ]; - size_t out_len = - net_buf_linearize(cereg_resp_str, sizeof(cereg_resp_str) - 1, data->rx_buf, 0, len); - cereg_resp_str[out_len] = '\0'; + deliver_pdu_parse(pdu_buffer, &pdu_data); - LOG_DBG("CEREG: %s", cereg_resp_str); - return 0; -} -#endif + uint8_t csms_idx = 0; -/** - * @brief Handler for getting PSM values - */ -MODEM_CMD_DEFINE(on_cmd_get_psm) -{ - size_t out_len = - net_buf_linearize(mdata.mdm_psm, sizeof(mdata.mdm_psm) - 1, data->rx_buf, 0, len); - mdata.mdm_psm[out_len] = '\0'; + if (pdu_data.udhl) { + char *udh = pdu_data.ud + 2; + uint8_t iei, iedl; - LOG_DBG("PSM: %s", mdata.mdm_psm); - return 0; -} + while (udh < (pdu_data.ud + 2 + (pdu_data.udhl * 2))) { + iei = hex_byte_to_data(udh); + udh += 2; + iedl = hex_byte_to_data(udh); + udh += 2; + if (iei != 0) { + LOG_WRN("Unknown UDH Identifier %d", iei); + udh += iedl * 2; + } else { + csms_data->csms_ref = hex_byte_to_data(udh); + udh += 2; + csms_data->csms_tot = hex_byte_to_data(udh); + udh += 2; + /* Todo: give some warning if we don't get all + * parts + */ + csms_idx = hex_byte_to_data(udh); + csms_data->csms_idx = csms_idx; + udh += 2; + } + } + } -/** - * @brief Handler for eDRX - */ -MODEM_CMD_DEFINE(on_cmd_get_edrx) -{ - size_t out_len = - net_buf_linearize(mdata.mdm_edrx, sizeof(mdata.mdm_edrx) - 1, data->rx_buf, 0, len); - mdata.mdm_edrx[out_len] = '\0'; + if (!strlen(sms->msg)) { + first_msg = true; + } else if (sms->csms_idx + 1 != csms_idx) { + return 0; + } - LOG_DBG("EDRX: %s", mdata.mdm_edrx); - return 0; -} + sms->csms_idx = csms_idx; -/** - * @brief Handler for LTECMD PTW - */ -MODEM_CMD_DEFINE(on_cmd_lte_ptw) -{ - strcpy(mdata.mdm_edrx, argv[1]); - return 0; -} + if (!first_msg) + goto decode_msg; /* We already have the phone number & timestamp */ -static char *get_4_octet(char *buf) -{ - char *ptr = buf; - uint16_t octCnt = 0; + int real_len = (pdu_data.oa_len % 2) ? pdu_data.oa_len + 1 : pdu_data.oa_len; - for (; octCnt < 4; octCnt++) { - if (ptr) { - ptr = strchr(ptr, '.'); - ++ptr; - } + for (int i = 0; i < real_len; i += 2) { + byteswp(&pdu_data.oa[i + 2], &pdu_data.oa[i + 3], 1); } - return ptr - 1; -} -/** - * @brief Set auto-connection mode on - */ -static int set_autoconn_on(void) -{ - static const char at_cmd[] = "AT\%SETACFG=modem_apps.Mode.AutoConnectMode,\"true\""; + memset(sms->phone, 0, sizeof(sms->phone)); + uint8_t type_of_number = (hex_byte_to_data(pdu_data.oa) >> 4) & 0x7; - LOG_WRN("autoconnect is set to false, will now set to true"); - int ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, NULL, 0, at_cmd, - &mdata.sem_response, MDM_CMD_RSP_TIME); - if (ret < 0) { - LOG_ERR("%s ret:%d", at_cmd, ret); + if (type_of_number == SMS_TON_INTERNATIONAL) { + sms->phone[0] = '+'; + memcpy(&sms->phone[1], pdu_data.oa + 2, pdu_data.oa_len); + } else { + memcpy(sms->phone, pdu_data.oa + 2, pdu_data.oa_len); } - return ret; -} -/** + for (int i = 0; i < 14; i += 2) { + byteswp(&pdu_data.scts[i], &pdu_data.scts[i + 1], 1); + } + + uint8_t tz = hex_byte_to_data(&pdu_data.scts[12]); + + snprintk(sms->time, sizeof(sms->time), "%.2s/%.2s/%.2s,%.2s:%.2s:%.2s%c%02x", pdu_data.scts, + &pdu_data.scts[2], &pdu_data.scts[4], &pdu_data.scts[6], &pdu_data.scts[8], + &pdu_data.scts[10], (tz & 0x80) ? '-' : '+', tz & 0x7F); + + memset(sms->msg, 0, sizeof(sms->msg)); + +decode_msg: + if (pdu_data.alphabet == SMS_ALPHABET_GSM8) { + if ((pdu_data.udl - pdu_data.udhl) > out_buf_avail) { + if (first_msg) { + LOG_WRN("Buffer too small: partial message " + "copied"); + } else { + LOG_WRN("Buffer too small: unable to " + "concatenate part %d", + csms_idx); + return 0; + } + } + hex_str_to_data(pdu_data.ud, out_buf, + MIN((out_buf_avail), pdu_data.udl - pdu_data.udhl)); + } else if (pdu_data.alphabet == SMS_ALPHABET_UCS2) { + uint16_t utf16_chr[3]; + char *ud = pdu_data.ud + (pdu_data.udhl ? 2 + 2 * pdu_data.udhl : 0); + char *out_buf_ptr = out_buf; + uint16_t *nchr; + int avail = out_buf_avail; + + utf16_chr[2] = 0; + while (strlen(ud) && avail) { + memset(utf16_chr, 0, sizeof(utf16_chr)); + hex_str_to_data(ud, (uint8_t *)utf16_chr, 4); + utf16_chr[0] = sys_be16_to_cpu(utf16_chr[0]); + utf16_chr[1] = sys_be16_to_cpu(utf16_chr[1]); + ret = utf16le_to_utf8(utf16_chr, out_buf_ptr, avail, &nchr); + if (ret) { + if (pdu_data.udhl && first_msg) { + LOG_WRN("Buffer too small: partial " + "message copied"); + break; + } + out_buf = '\0'; + LOG_WRN("Buffer too small: unable to " + "concatenate part %d", + csms_idx); + return 0; + } + avail -= strlen(out_buf_ptr); + out_buf_ptr += strlen(out_buf_ptr); + if (nchr == &utf16_chr[1]) { + ud += 4; + } else { + ud += 8; + } + } + } else if (pdu_data.alphabet == SMS_ALPHABET_GSM7) { + if (!pdu_data.udhl) { + ret = gsm7_decode(pdu_data.ud, pdu_data.udl, out_buf, out_buf_avail, 0); + if (ret) { + LOG_WRN("Buffer too small: partial message " + "copied"); + } + } else { + uint8_t skip = ((pdu_data.udhl + 1) * 8 + 6) / 7; + + ret = gsm7_decode(pdu_data.ud, pdu_data.udl, out_buf, out_buf_avail, skip); + if (ret && !first_msg) { + LOG_WRN("Buffer too small: unable to " + "concatenate part %d", + csms_idx); + *out_buf = '\0'; + return 0; + } else if (ret) { + LOG_WRN("Buffer too small: partial message " + "copied"); + } + } + } + return 0; +} + + +/** + * Check if given char sequence is crlf. + * + * @param c The char sequence. + * @param len Total length of the fragment. + * @return @c true if char sequence is crlf. + * Otherwise @c false is returned. + */ +static bool is_crlf(uint8_t *c, uint8_t len) +{ + /* crlf does not fit. */ + if (len < 2) { + return false; + } + + return c[0] == '\r' && c[1] == '\n'; +} + +/** + * Find terminating crlf in a netbuffer. + * + * @param buf The netbuffer. + * @param skip Bytes to skip before search. + * @return Length of the returned fragment or 0 if not found. + */ +static size_t net_buf_find_crlf(struct net_buf *buf, size_t skip) +{ + size_t len = 0, pos = 0; + struct net_buf *frag = buf; + + /* Skip to the start. */ + while (frag && skip >= frag->len) { + skip -= frag->len; + frag = frag->frags; + } + + /* Need to wait for more data. */ + if (!frag) { + return 0; + } + + pos = skip; + + while (frag && !is_crlf(frag->data + pos, frag->len - pos)) { + if (pos + 1 >= frag->len) { + len += frag->len; + frag = frag->frags; + pos = 0U; + } else { + pos++; + } + } + + if (frag && is_crlf(frag->data + pos, frag->len - pos)) { + len += pos; + return len - skip; + } + + return 0; +} + +/** + * @brief Handler for receiving unsolicited SMS message indication (+CMTI) + */ +MODEM_CMD_DEFINE(on_cmd_unsol_sms) +{ + LOG_DBG("SMS Received"); + k_sem_give(&mdata.sem_rcv_sms); + + return 0; +} + +#if defined(CONFIG_MODEM_SMS_CALLBACK) + +static struct sms_in async_sms_in; + +/** + * @brief Handler for receiving unsolicited SMS messages (+CMT) + */ +MODEM_CMD_DEFINE(on_cmd_unsol_cmt) +{ + int ret; + char pdu_buffer[360]; + struct csms_data_s csms_data; + size_t out_len, sms_len, param_len; + + /* Get the length of the "length" parameter. + * The last parameter will be stuck in the netbuffer. + * It is not the actual length of the trailing pdu so + * we have to search the next crlf. + */ + param_len = net_buf_find_crlf(data->rx_buf, 0); + if (param_len == 0) { + LOG_DBG("No "); + return -EAGAIN; + } + + /* Get actual trailing pdu len. +2 to skip crlf. */ + sms_len = net_buf_find_crlf(data->rx_buf, param_len + 2); + if (sms_len == 0) { + return -EAGAIN; + } + + /* Skip to start of pdu. */ + data->rx_buf = net_buf_skip(data->rx_buf, param_len + 2); + out_len = net_buf_linearize(pdu_buffer, sizeof(pdu_buffer), data->rx_buf, 0, sms_len); + pdu_buffer[out_len] = '\0'; + + LOG_DBG("SMS Received: %s", pdu_buffer); + + data->rx_buf = net_buf_skip(data->rx_buf, sms_len); + + memset(&async_sms_in, 0, sizeof(async_sms_in)); + + mdata.sms = &async_sms_in; + + pdu_msg_extract(pdu_buffer, &csms_data); + + notify_sms_recv(mdata.net_iface->if_dev->dev, mdata.sms, csms_data.csms_ref, + csms_data.csms_idx, csms_data.csms_tot); + + return ret; +} +#endif /* CONFIG_MODEM_SMS_CALLBACK */ +#endif /* CONFIG_MODEM_SMS */ + +/** + * @brief Handler for unsolicited events ( SOCKETEV) + */ +MODEM_CMD_DEFINE(on_cmd_unsol_SEV) +{ + struct modem_socket *sock; + int sock_fd; + int evt_id; + + LOG_DBG("got unsolicit socketev, evt: %s, sockfd: %s", argv[0], argv[1]); + evt_id = ATOI(argv[0], 0, "event_id"); + sock_fd = ATOI(argv[1], 0, "sock_fd"); + /* TODO - handle optional connected fd */ + sock = modem_socket_from_fd(&mdata.socket_config, sock_fd); + if (!sock) { + return 0; + } + + /* Data ready indication. */ + switch (evt_id) { + case 0: /* in execution */ + break; + case 1: /* Rx Rdy */ + LOG_DBG("Data Receive Indication for socket: %d", sock_fd); + + modem_socket_packet_size_update(&mdata.socket_config, sock, 1); + modem_socket_data_ready(&mdata.socket_config, sock); + + break; + /* TODO: need to save the indication that the socket has been + * terminated remotely and treat properly in send and recv + * functions + */ + case 2: /* socket deactivated */ + case 3: /* socket terminated */ + LOG_WRN("Remote peer closed for socket: %d", sock_fd); + break; + case 4: /* socket accepted */ + break; + case 6: /* socket activation done */ + break; + default: + break; + } + + return 0; +} + +/** + * @brief Handler for manufacturer + */ +MODEM_CMD_DEFINE(on_cmd_get_manufacturer) +{ + modem_cmd_handler_set_error(data, 0); + + size_t out_len = net_buf_linearize( + mdata.mdm_manufacturer, sizeof(mdata.mdm_manufacturer) - 1, data->rx_buf, 0, len); + mdata.mdm_manufacturer[out_len] = '\0'; + LOG_DBG("Manufacturer: %s", mdata.mdm_manufacturer); + return 0; +} + +/** + * @brief Handler for model + */ +MODEM_CMD_DEFINE(on_cmd_get_model) +{ + size_t out_len = net_buf_linearize(mdata.mdm_model, sizeof(mdata.mdm_model) - 1, + data->rx_buf, 0, len); + mdata.mdm_model[out_len] = '\0'; + + LOG_DBG("Model: %s", mdata.mdm_model); + return 0; +} + +/** + * @brief Handler for IMEI + */ +MODEM_CMD_DEFINE(on_cmd_get_imei) +{ + size_t out_len = + net_buf_linearize(mdata.mdm_imei, sizeof(mdata.mdm_imei) - 1, data->rx_buf, 0, len); + mdata.mdm_imei[out_len] = '\0'; + + LOG_DBG("IMEI: %s", mdata.mdm_imei); + return 0; +} + +#if defined(CONFIG_MODEM_SIM_NUMBERS) +/** + * @brief Handler for IMSI + */ +MODEM_CMD_DEFINE(on_cmd_get_imsi) +{ + size_t out_len = + net_buf_linearize(mdata.mdm_imsi, sizeof(mdata.mdm_imsi) - 1, data->rx_buf, 0, len); + mdata.mdm_imsi[out_len] = '\0'; + + LOG_DBG("IMSI: %s", mdata.mdm_imsi); + return 0; +} + +/** + * @brief Handler for ICCID + */ +MODEM_CMD_DEFINE(on_cmd_get_iccid) +{ + size_t out_len = net_buf_linearize(mdata.mdm_iccid, sizeof(mdata.mdm_iccid) - 1, + data->rx_buf, 0, len); + mdata.mdm_iccid[out_len] = '\0'; + + LOG_DBG("ICCID: %s", mdata.mdm_iccid); + return 0; +} +#endif /* defined(CONFIG_MODEM_SIM_NUMBERS) */ + +/** + * @brief Handler for BAND info + */ +MODEM_CMD_DEFINE(on_cmd_get_bands) +{ +#define MAX_BANDS_STR_SZ 64 + char bandstr[MAX_BANDS_STR_SZ]; + size_t out_len = net_buf_linearize(bandstr, sizeof(bandstr) - 1, data->rx_buf, 0, len); + + bandstr[out_len] = '\0'; + + LOG_DBG("BANDS - %s", bandstr); + + return 0; +} + +/** + * @brief Handler for GETACFG=modem_apps.Mode.AutoConnectMode + */ +static bool needto_set_autoconn_to_true; +MODEM_CMD_DEFINE(on_cmd_get_acfg) +{ +#define MAX_AUTOCONN_STR_SZ 16 + char autoconnmode_str[MAX_AUTOCONN_STR_SZ]; + size_t out_len = net_buf_linearize(autoconnmode_str, sizeof(autoconnmode_str) - 1, + data->rx_buf, 0, len); + autoconnmode_str[out_len] = '\0'; + + if (strncmp(autoconnmode_str, "false", strlen("false")) == 0) { + needto_set_autoconn_to_true = true; + } else { + needto_set_autoconn_to_true = false; + LOG_DBG("Auto Conn Mode: %s", autoconnmode_str); + } + return 0; +} + +/** + * @brief Handler for socket count info + */ +static bool needto_set_sockcount; +MODEM_CMD_DEFINE(on_cmd_get_sockcount) +{ + char sockcount_str[16]; + size_t out_len = + net_buf_linearize(sockcount_str, sizeof(sockcount_str) - 1, data->rx_buf, 0, len); + sockcount_str[out_len] = '\0'; + + if (strtol(sockcount_str, NULL, 10) != MDM_MAX_SOCKETS) { + needto_set_sockcount = true; + } else { + needto_set_sockcount = false; + } + return 0; +} + +#ifdef VERIFY_INIT_MODEM_STATE +/** + * @brief Handler for CFUN + */ +MODEM_CMD_DEFINE(on_cmd_get_cfun) +{ +#define MAX_CFUN_STR_SZ 16 + char cfun_resp_str[MAX_CFUN_STR_SZ]; + size_t out_len = + net_buf_linearize(cfun_resp_str, sizeof(cfun_resp_str) - 1, data->rx_buf, 0, len); + cfun_resp_str[out_len] = '\0'; + + LOG_DBG("CFUN: %s", cfun_resp_str); + return 0; +} + +/** + * @brief Handler for CEREG + */ +MODEM_CMD_DEFINE(on_cmd_get_cereg) +{ +#define MAX_CEREG_STR_SZ 16 + char cereg_resp_str[MAX_CEREG_STR_SZ]; + size_t out_len = + net_buf_linearize(cereg_resp_str, sizeof(cereg_resp_str) - 1, data->rx_buf, 0, len); + cereg_resp_str[out_len] = '\0'; + + LOG_DBG("CEREG: %s", cereg_resp_str); + return 0; +} +#endif + +/** + * @brief Handler for getting PSM values + */ +MODEM_CMD_DEFINE(on_cmd_get_psm) +{ + size_t out_len = + net_buf_linearize(mdata.mdm_psm, sizeof(mdata.mdm_psm) - 1, data->rx_buf, 0, len); + mdata.mdm_psm[out_len] = '\0'; + + LOG_DBG("PSM: %s", mdata.mdm_psm); + return 0; +} + +/** + * @brief Handler for eDRX + */ +MODEM_CMD_DEFINE(on_cmd_get_edrx) +{ + size_t out_len = + net_buf_linearize(mdata.mdm_edrx, sizeof(mdata.mdm_edrx) - 1, data->rx_buf, 0, len); + mdata.mdm_edrx[out_len] = '\0'; + + LOG_DBG("EDRX: %s", mdata.mdm_edrx); + return 0; +} + +/** + * @brief Handler for LTECMD PTW + */ +MODEM_CMD_DEFINE(on_cmd_lte_ptw) +{ + strcpy(mdata.mdm_edrx, argv[1]); + return 0; +} + +static char *get_4_octet(char *buf) +{ + char *ptr = buf; + uint16_t octCnt = 0; + + for (; octCnt < 4; octCnt++) { + if (ptr) { + ptr = strchr(ptr, '.'); + ++ptr; + } + } + return ptr - 1; +} + +/** + * @brief Set auto-connection mode on + */ +static int set_autoconn_on(void) +{ + static const char at_cmd[] = "AT\%SETACFG=modem_apps.Mode.AutoConnectMode,\"true\""; + + LOG_WRN("autoconnect is set to false, will now set to true"); + int ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, NULL, 0, at_cmd, + &mdata.sem_response, MDM_CMD_RSP_TIME); + if (ret < 0) { + LOG_ERR("%s ret:%d", at_cmd, ret); + } + return ret; +} + +/** * @brief Set socket count to match config */ static int set_socket_count(void) @@ -1718,6 +2281,7 @@ MODEM_CMD_DEFINE(on_cmd_sock_readdata) int more = (int)strtol(argv[2], NULL, 10); int ret = on_cmd_sockread_common(mdata.sock_fd, data, ATOI(argv[1], 0, "length"), len); + LOG_DBG("on_cmd_sockread_common returned %d", ret); if (more) { @@ -1736,7 +2300,12 @@ static const struct modem_cmd response_cmds[] = { static const struct modem_cmd unsol_cmds[] = { MODEM_CMD("%SOCKETEV:", on_cmd_unsol_SEV, 2U, ","), +#if defined(CONFIG_MODEM_SMS) MODEM_CMD("+CMTI:", on_cmd_unsol_sms, 2U, ","), +#if defined(CONFIG_MODEM_SMS_CALLBACK) + MODEM_CMD("+CMT:", on_cmd_unsol_cmt, 2U, ",\r"), +#endif /* CONFIG_MODEM_SMS_CALLBACK */ +#endif /* CONFIG_MODEM_SMS */ }; /** @@ -1840,509 +2409,157 @@ static int check_mdm_store_file(const char *filename) LOG_ERR("%s ret:%d", at_cmd, ret); ret = -1; } - if (!file_found) { - return -1; - } - - return ret; -} - -/** - * @brief Misc init after normal mdm init - */ -static int post_mdm_init(void) -{ - - int ret = 0; - return ret; -} -#endif - -/** - * @brief Handler for AT+COPS? - */ -MODEM_CMD_DEFINE(on_cmd_cops) -{ - char buf[32]; - int sz; - size_t out_len = net_buf_linearize(buf, sizeof(buf) - 1, data->rx_buf, 0, len); - buf[out_len] = '\0'; - - LOG_DBG("full cops: %s", buf); - sz = get_str_in_quotes(buf, mdata.mdm_carrier, sizeof(mdata.mdm_carrier)); - - LOG_DBG("Carrier: %s", mdata.mdm_carrier); - - sz ? (errno = 0) : (errno = EINVAL); - return sz ? 0 : -1; -} - -/** - * @brief Get connection status - */ -static int get_carrier(char *rbuf) -{ - int ret; - static const char at_cmd[] = "AT+COPS?"; - struct modem_cmd data_cmd[] = { - MODEM_CMD("+COPS:", on_cmd_cops, 0U, ","), - }; - - ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, data_cmd, ARRAY_SIZE(data_cmd), at_cmd, - &mdata.sem_response, MDM_CMD_LONG_RSP_TIME); - if (ret < 0) { - LOG_ERR("%s ret:%d", at_cmd, ret); - ret = -1; - } - snprintk(rbuf, MAX_CARRIER_RESP_SIZE, "%s", mdata.mdm_carrier); - return ret; -} - -/** - * @brief Get PSM - */ -static int get_psm(char *response) -{ - int ret; - static const char at_cmd[] = "AT+CPSMS?"; - struct modem_cmd data_cmd[] = { - MODEM_CMD("+CPSMS:", on_cmd_get_psm, 0U, ","), - }; - ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, data_cmd, ARRAY_SIZE(data_cmd), at_cmd, - &mdata.sem_response, MDM_CMD_RSP_TIME); - if (ret < 0) { - LOG_ERR("%s ret:%d", at_cmd, ret); - ret = -1; - } - snprintk(response, MAX_PSM_RESP_SIZE, "%s", mdata.mdm_psm); - return ret; -} - -/** - * @brief Get edrx - */ -static int get_edrx(char *response) -{ - int ret; - static const char at_cmd[] = "AT+CEDRXS?"; - struct modem_cmd data_cmd[] = { - MODEM_CMD("+CEDRXS:", on_cmd_get_edrx, 0U, ","), - }; - - memset(mdata.mdm_edrx, 0, sizeof(mdata.mdm_edrx)); - - ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, data_cmd, ARRAY_SIZE(data_cmd), at_cmd, - &mdata.sem_response, MDM_CMD_RSP_TIME); - if (ret < 0) { - LOG_ERR("%s ret:%d", at_cmd, ret); - ret = -1; - } - snprintk(response, MAX_EDRX_RESP_SIZE, "%s", mdata.mdm_edrx); - return ret; -} - -/** - * @brief Reset the modem - */ -static int reset_modem(void) -{ - static const char at_cmd[] = "ATZ"; - int ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, NULL, 0, at_cmd, - &mdata.sem_response, MDM_CMD_RSP_TIME); - if (ret < 0) { - LOG_ERR("Error rebooting modem"); - } else { - LOG_INF("Waiting %d secs for modem to boot...", MDM_BOOT_DELAY); - k_sleep(K_SECONDS(MDM_BOOT_DELAY)); - } - return ret; -} - -/** - * @brief Close the given socket - */ -static void socket_close(struct modem_socket *sock) -{ - char at_cmd[40]; - int ret; - - if (sock->sock_fd != -1) { - - /* Tell the modem to close the socket. */ - snprintk(at_cmd, sizeof(at_cmd), "AT%%SOCKETCMD=\"DEACTIVATE\",%d", sock->sock_fd); - ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, NULL, 0U, at_cmd, - &mdata.sem_response, MDM_CMD_RSP_TIME); - - if (ret < 0) { - LOG_ERR("%s ret:%d", at_cmd, ret); - } - - /* Tell the modem to delete the socket. */ - snprintk(at_cmd, sizeof(at_cmd), "AT%%SOCKETCMD=\"DELETE\",%d", sock->sock_fd); - ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, NULL, 0U, at_cmd, - &mdata.sem_response, MDM_CMD_RSP_TIME); - if (ret < 0) { - LOG_ERR("%s ret:%d", at_cmd, ret); - } - } - modem_socket_put(&mdata.socket_config, sock->sock_fd); -} - -/** - * @brief Send an sms message - */ -static int send_sms_msg(void *obj, const struct sms_out *sms) -{ - /* The "+ 20" is to account for AT+CMGS plus a bit extra */ - char at_cmd[sizeof(struct sms_out) + 21]; - int ret; - - k_sem_take(&mdata.sem_sms, K_FOREVER); - - snprintk(at_cmd, sizeof(at_cmd), "AT+CMGF=1"); - ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, NULL, 0U, at_cmd, &mdata.sem_response, - MDM_CMD_RSP_TIME); - if (ret < 0) { - LOG_ERR("%s ret:%d", at_cmd, ret); - } - - snprintk(at_cmd, sizeof(at_cmd), "AT%%CMGSC=\"%s\"\r%s\x1a", sms->phone, sms->msg); - ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, NULL, 0U, at_cmd, &mdata.sem_response, - MDM_CMD_LONG_RSP_TIME); - if (ret < 0) { - LOG_ERR("%s ret:%d", at_cmd, ret); - } - - k_sem_give(&mdata.sem_sms); - - return ret; -} - -enum sms_tp_flags { - TP_FLAG_MMS = BIT(2), - TP_FLAG_RP = BIT(7), - TP_FLAG_UDHI = BIT(6), - TP_FLAG_SRI = BIT(5) -}; - -enum sms_type_of_number { - SMS_TON_UNKNOWN = 0, - SMS_TON_INTERNATIONAL, - SMS_TON_NATIONAL, - SMS_TON_NETWORK_SPECIFIC, - SMS_TON_SUBSCRIBER, - SMS_TON_ALPHANUMERIC, - SMS_TON_ABBREVIATED, - SMS_TON_RESERVED, -}; - -enum sms_alphabet { - SMS_ALPHABET_GSM7 = 0, - SMS_ALPHABET_GSM8, - SMS_ALPHABET_UCS2, -}; - -/* Structure for storing information about a SMS-DELIVER PDU */ -struct deliver_pdu_data_s { - uint8_t smsc_len; /* Length of SMSC segment */ - char *smsc_start; /* SMSC segment */ - uint8_t tp_flags; /* Flags */ - uint8_t oa_len; /* Originator address length */ - char *oa; /* Originator address */ - uint8_t alphabet; /* Alphabet used */ - char *scts; /* Timestamp */ - uint8_t udl; /* User data length */ - uint8_t udhl; /* User data header length */ - char *ud; /* User data */ -}; - -/** - * @brief Function for parsing the PDU structure for a SMS Deliver PDU - * - * @param buf Raw PDU buffer - * @param pdu_data Output structure - */ -void deliver_pdu_parse(char *buf, struct deliver_pdu_data_s *pdu_data) -{ - pdu_data->smsc_len = hex_byte_to_data(buf); - pdu_data->smsc_start = pdu_data->smsc_len ? buf + 2 : NULL; - buf += 2 + pdu_data->smsc_len * 2; - pdu_data->tp_flags = hex_byte_to_data(buf) & ~0x3; - buf += 2; - pdu_data->oa_len = hex_byte_to_data(buf); - buf += 2; - pdu_data->oa = buf; - if ((hex_byte_to_data(buf) & 0x70) != 0x50) { - buf += 2 + pdu_data->oa_len; - buf += (pdu_data->oa_len % 2); - } else { - buf += 2 + (((pdu_data->oa_len + 1) * 7 / 8) * 2); + if (!file_found) { + return -1; } - buf += 2; /* Skip TP-PID */ - pdu_data->alphabet = (hex_byte_to_data(buf) & 0xC) >> 2; - buf += 2; - pdu_data->scts = buf; - buf += 14; - pdu_data->udl = hex_byte_to_data(buf); - buf += 2; - pdu_data->ud = buf; - pdu_data->udhl = (pdu_data->tp_flags & TP_FLAG_UDHI) ? hex_byte_to_data(buf) : 0; + + return ret; } /** - * @brief Logic to unpack septets - * - * @param frag Packed septet framents - * @param frag_len Length of packed fragments - * @param out Output: Septets, unpacked into an octet (byte) array + * @brief Misc init after normal mdm init */ -void gsmunpack_frag(uint8_t *frag, int frag_len, uint8_t *out) +static int post_mdm_init(void) { - switch (frag_len) { - case 7: - out[7] = frag[6] >> 1; - out[6] = (frag[5] >> 2) | ((frag[6] & 0x01) << 6); - case 6: - out[5] = (frag[4] >> 3) | ((frag[5] & 0x03) << 5); - case 5: - out[4] = (frag[3] >> 4) | ((frag[4] & 0x07) << 4); - case 4: - out[3] = (frag[2] >> 5) | ((frag[3] & 0x0F) << 3); - case 3: - out[2] = (frag[1] >> 6) | ((frag[2] & 0x1F) << 2); - case 2: - out[1] = (frag[0] >> 7) | ((frag[1] & 0x3F) << 1); - case 1: - out[0] = frag[0] & 0x7F; - } + + int ret = 0; + return ret; } +#endif /** - * @brief Logic for converting GSM 7-bit characters into ASCII - * - * @param gsm GSM septet - * @param escaped Flag for if the previous character was an escape character - * @return char ASCII equivalent character + * @brief Handler for AT+COPS? */ -char gsm2ascii(uint8_t gsm, bool escaped) +MODEM_CMD_DEFINE(on_cmd_cops) { - if (escaped) { - switch (gsm) { - case 0x0A: - return '\f'; - case 0x14: - return '^'; - case 0x28: - return '{'; - case 0x29: - return '}'; - case 0x2F: - return '\\'; - case 0x3c: - return '['; - case 0x3d: - return '~'; - case 0x3e: - return ']'; - case 0x40: - return '|'; - default: - return '\0'; - } - } - if ((gsm >= 'a' && gsm <= 'z') || (gsm >= 'A' && gsm <= 'Z') || (gsm >> 4) == 3 || - (((gsm >> 4) == 2) && gsm != 0x24)) { - return gsm; - } - switch (gsm) { - case 0x00: - return '@'; - case 0x02: - return '$'; - case '\n': - case '\r': - return gsm; - case 0x11: - return '_'; - default: - return '\0'; - } + char buf[32]; + int sz; + size_t out_len = net_buf_linearize(buf, sizeof(buf) - 1, data->rx_buf, 0, len); + + buf[out_len] = '\0'; + + LOG_DBG("full cops: %s", buf); + sz = get_str_in_quotes(buf, mdata.mdm_carrier, sizeof(mdata.mdm_carrier)); + + LOG_DBG("Carrier: %s", mdata.mdm_carrier); + + sz ? (errno = 0) : (errno = EINVAL); + return sz ? 0 : -1; } /** - * @brief Combined logic for converting a septet payload into an ASCII string - * - * @param in Binary encoding of the septet payload/SMS User Data - * @param udl Length of septet payload/SMS User Data - * @param out Buffer for output - * @param outlen Length of output buffer - * @param skip Number of septets to skip - * @return int 0 on success + * @brief Get connection status */ -int gsm7_decode(char *in, int udl, char *out, int outlen, int skip) +static int get_carrier(char *rbuf) { - uint8_t packed[7], unpacked[8]; - uint8_t processed = 0, escaped_cnt = 0; - uint8_t udl_octets = ((udl * 7 + 7) / 8); - int skip_drp = skip; - char *out_orig = out; - bool escaped = false; + int ret; + static const char at_cmd[] = "AT+COPS?"; + struct modem_cmd data_cmd[] = { + MODEM_CMD("+COPS:", on_cmd_cops, 0U, ","), + }; - for (int i = 0; i < udl_octets; i += 7) { - memset(packed, 0, 7); - hex_str_to_data(&in[i * 2], packed, MIN(7, udl_octets - processed)); - gsmunpack_frag(packed, MIN(7, udl_octets - processed), unpacked); - processed += 7; - for (int j = 0; j < 8; j++) { - if (skip) { - skip--; - } else if (unpacked[j] == 0x1b) { - escaped = true; - escaped_cnt++; - } else { - if (out > (out_orig + outlen - 1)) { - return 1; - } - char chr = gsm2ascii(unpacked[j], escaped); + ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, data_cmd, ARRAY_SIZE(data_cmd), at_cmd, + &mdata.sem_response, MDM_CMD_LONG_RSP_TIME); + if (ret < 0) { + LOG_ERR("%s ret:%d", at_cmd, ret); + ret = -1; + } + snprintk(rbuf, MAX_CARRIER_RESP_SIZE, "%s", mdata.mdm_carrier); + return ret; +} - if (chr) { - *out = chr; - out++; - } - escaped = false; - } - } +/** + * @brief Get PSM + */ +static int get_psm(char *response) +{ + int ret; + static const char at_cmd[] = "AT+CPSMS?"; + struct modem_cmd data_cmd[] = { + MODEM_CMD("+CPSMS:", on_cmd_get_psm, 0U, ","), + }; + ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, data_cmd, ARRAY_SIZE(data_cmd), at_cmd, + &mdata.sem_response, MDM_CMD_RSP_TIME); + if (ret < 0) { + LOG_ERR("%s ret:%d", at_cmd, ret); + ret = -1; } - out_orig[MIN((udl - escaped_cnt - skip_drp), outlen)] = '\0'; - return 0; + snprintk(response, MAX_PSM_RESP_SIZE, "%s", mdata.mdm_psm); + return ret; } /** - * @brief Function for converting a UTF-16LE encoded character into UTF-8 - * - * @param in Pointer to UTF-16 character - * @param out Pointer to UTF-8 output buffer - * @param out_len Length of output buffer - * @param next_char Pointer to new position in output buffer - * @return int 0 on success else negative errno code + * @brief Get edrx */ -int utf16le_to_utf8(uint16_t *in, uint8_t *out, size_t out_len, uint16_t **next_char) +static int get_edrx(char *response) { - uint32_t codepoint; + int ret; + static const char at_cmd[] = "AT+CEDRXS?"; + struct modem_cmd data_cmd[] = { + MODEM_CMD("+CEDRXS:", on_cmd_get_edrx, 0U, ","), + }; - if (!out || !in || !out_len || !*in) { - return -EINVAL; - } - if (in[1] != 0 && ((sys_le16_to_cpu(in[0]) & 0xFC00) == 0xD800) && - (sys_le16_to_cpu(in[1]) & 0xFC00) == 0xDC00) { - codepoint = (sys_le16_to_cpu(in[0]) - 0xD800) << 10; - codepoint += sys_le16_to_cpu(in[1]) - 0xDC00; - codepoint += 0x10000; - } else { - codepoint = in[0]; - } + memset(mdata.mdm_edrx, 0, sizeof(mdata.mdm_edrx)); - if (codepoint <= 0x7F) { - *out = (uint8_t)codepoint; - out++; - } else if (codepoint <= 0x7FF) { - if (out_len < 2) { - return -EIO; - } - *out = 0xC0 | (codepoint >> 6); - out++; - *out = 0x80 | (codepoint & 0x3F); - out++; - } else if (codepoint <= 0xFFFF) { - if (out_len < 3) { - return -EIO; - } - *out = 0xE0 | (codepoint >> 12); - out++; - *out = 0x80 | ((codepoint >> 6) & 0x3F); - out++; - *out = 0x80 | (codepoint & 0x3F); - out++; - } else { - if (out_len < 4) { - return -EIO; - } - *out = 0xF0 | (codepoint >> 18); - out++; - *out = 0x80 | ((codepoint >> 12) & 0x3F); - out++; - *out = 0x80 | ((codepoint >> 6) & 0x3F); - out++; - *out = 0x80 | (codepoint & 0x3F); - out++; - } - if (next_char) { - *next_char = codepoint > 65535 ? &in[2] : &in[1]; + ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, data_cmd, ARRAY_SIZE(data_cmd), at_cmd, + &mdata.sem_response, MDM_CMD_RSP_TIME); + if (ret < 0) { + LOG_ERR("%s ret:%d", at_cmd, ret); + ret = -1; } - return 0; + snprintk(response, MAX_EDRX_RESP_SIZE, "%s", mdata.mdm_edrx); + return ret; } -/** - * Check if given char sequence is crlf. - * - * @param c The char sequence. - * @param len Total length of the fragment. - * @return @c true if char sequence is crlf. - * Otherwise @c false is returned. +/** + * @brief Reset the modem */ -static bool is_crlf(uint8_t *c, uint8_t len) +static int reset_modem(void) { - /* crlf does not fit. */ - if (len < 2) { - return false; + static const char at_cmd[] = "ATZ"; + int ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, NULL, 0, at_cmd, + &mdata.sem_response, MDM_CMD_RSP_TIME); + if (ret < 0) { + LOG_ERR("Error rebooting modem"); + } else { + LOG_INF("Waiting %d secs for modem to boot...", MDM_BOOT_DELAY); + k_sleep(K_SECONDS(MDM_BOOT_DELAY)); } - - return c[0] == '\r' && c[1] == '\n'; + return ret; } /** - * Find terminating crlf in a netbuffer. - * - * @param buf The netbuffer. - * @param skip Bytes to skip before search. - * @return Length of the returned fragment or 0 if not found. + * @brief Close the given socket */ -static size_t net_buf_find_crlf(struct net_buf *buf, size_t skip) +static void socket_close(struct modem_socket *sock) { - size_t len = 0, pos = 0; - struct net_buf *frag = buf; - - /* Skip to the start. */ - while (frag && skip >= frag->len) { - skip -= frag->len; - frag = frag->frags; - } + char at_cmd[40]; + int ret; - /* Need to wait for more data. */ - if (!frag) { - return 0; - } + if (sock->sock_fd != -1) { - pos = skip; + /* Tell the modem to close the socket. */ + snprintk(at_cmd, sizeof(at_cmd), "AT%%SOCKETCMD=\"DEACTIVATE\",%d", sock->sock_fd); + ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, NULL, 0U, at_cmd, + &mdata.sem_response, MDM_CMD_RSP_TIME); - while (frag && !is_crlf(frag->data + pos, frag->len - pos)) { - if (pos + 1 >= frag->len) { - len += frag->len; - frag = frag->frags; - pos = 0U; - } else { - pos++; + if (ret < 0) { + LOG_ERR("%s ret:%d", at_cmd, ret); } - } - if (frag && is_crlf(frag->data + pos, frag->len - pos)) { - len += pos; - return len - skip; + /* Tell the modem to delete the socket. */ + snprintk(at_cmd, sizeof(at_cmd), "AT%%SOCKETCMD=\"DELETE\",%d", sock->sock_fd); + ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, NULL, 0U, at_cmd, + &mdata.sem_response, MDM_CMD_RSP_TIME); + if (ret < 0) { + LOG_ERR("%s ret:%d", at_cmd, ret); + } } - - return 0; + modem_socket_put(&mdata.socket_config, sock->sock_fd); } +#if defined(CONFIG_MODEM_SMS) + /** * Parses list sms and add them to buffer. * Format is: @@ -2498,13 +2715,8 @@ MODEM_CMD_DEFINE(on_cmd_cmgl) */ MODEM_CMD_DEFINE(on_cmd_cmgr) { - int ret; char pdu_buffer[360]; size_t out_len, sms_len, param_len; - struct sms_in *sms; - bool first_msg = false; - char *out_buf; - size_t out_buf_avail; /* Get the length of the "length" parameter. * The last parameter will be stuck in the netbuffer. @@ -2530,154 +2742,53 @@ MODEM_CMD_DEFINE(on_cmd_cmgr) data->rx_buf = net_buf_skip(data->rx_buf, sms_len); - /* No buffer specified. */ - if (!mdata.sms) { - return 0; - } - sms = mdata.sms; - out_buf = sms->msg; - out_buf += strlen(sms->msg); - - out_buf_avail = sizeof(sms->msg) - (strlen(sms->msg) + 1); - struct deliver_pdu_data_s pdu_data; - - deliver_pdu_parse(pdu_buffer, &pdu_data); - - uint8_t csms_idx = 0; + return pdu_msg_extract(pdu_buffer, NULL); +} - if (pdu_data.udhl) { - char *udh = pdu_data.ud + 2; - uint8_t iei, iedl; +/** + * @brief Send an sms message + */ +static int send_sms_msg(const struct sms_out *sms) +{ + /* The "+ 20" is to account for AT+CMGS plus a bit extra */ + char at_cmd[sizeof(struct sms_out) + 21]; + int ret; - while (udh < (pdu_data.ud + 2 + (pdu_data.udhl * 2))) { - iei = hex_byte_to_data(udh); - udh += 2; - iedl = hex_byte_to_data(udh); - udh += 2; - if (iei != 0) { - LOG_WRN("Unknown UDH Identifier %d", iei); - udh += iedl * 2; - } else { - udh += 4; - /* Todo: give some warning if we don't get all - * parts - */ - csms_idx = hex_byte_to_data(udh); - udh += 2; - } - } - } - if (!strlen(sms->msg)) { - first_msg = true; - } else if (sms->csms_idx + 1 != csms_idx) { - return 0; - } - sms->csms_idx = csms_idx; + k_sem_take(&mdata.sem_sms, K_FOREVER); - if (!first_msg) - goto decode_msg; /* We already have the phone number & timestamp - */ - int real_len = (pdu_data.oa_len % 2) ? pdu_data.oa_len + 1 : pdu_data.oa_len; - for (int i = 0; i < real_len; i += 2) { - byteswp(&pdu_data.oa[i + 2], &pdu_data.oa[i + 3], 1); + snprintk(at_cmd, sizeof(at_cmd), "AT+CMGF=1"); + ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, NULL, 0U, at_cmd, &mdata.sem_response, + MDM_CMD_LONG_RSP_TIME); + if (ret < 0) { + LOG_ERR("%s ret:%d", at_cmd, ret); } - memset(sms->phone, 0, sizeof(sms->phone)); - uint8_t type_of_number = (hex_byte_to_data(pdu_data.oa) >> 4) & 0x7; - if (type_of_number == SMS_TON_INTERNATIONAL) { - sms->phone[0] = '+'; - memcpy(&sms->phone[1], pdu_data.oa + 2, pdu_data.oa_len); - } else { - memcpy(sms->phone, pdu_data.oa + 2, pdu_data.oa_len); - } - for (int i = 0; i < 14; i += 2) { - byteswp(&pdu_data.scts[i], &pdu_data.scts[i + 1], 1); + snprintk(at_cmd, sizeof(at_cmd), "AT%%CMGSC=\"%s\"\r%s\x1a", sms->phone, sms->msg); + ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, NULL, 0U, at_cmd, &mdata.sem_response, + MDM_CMD_LONG_RSP_TIME); + if (ret < 0) { + LOG_ERR("%s ret:%d", at_cmd, ret); } - uint8_t tz = hex_byte_to_data(&pdu_data.scts[12]); - snprintk(sms->time, sizeof(sms->time), "%.2s/%.2s/%.2s,%.2s:%.2s:%.2s%c%02x", pdu_data.scts, - &pdu_data.scts[2], &pdu_data.scts[4], &pdu_data.scts[6], &pdu_data.scts[8], - &pdu_data.scts[10], (tz & 0x80) ? '-' : '+', tz & 0x7F); - memset(sms->msg, 0, sizeof(sms->msg)); + k_msleep(100); -decode_msg: - if (pdu_data.alphabet == SMS_ALPHABET_GSM8) { - if ((pdu_data.udl - pdu_data.udhl) > out_buf_avail) { - if (first_msg) { - LOG_WRN("Buffer too small: partial message " - "copied"); - } else { - LOG_WRN("Buffer too small: unable to " - "concatenate part %d", - csms_idx); - return 0; - } - } - hex_str_to_data(pdu_data.ud, out_buf, - MIN((out_buf_avail), pdu_data.udl - pdu_data.udhl)); - } else if (pdu_data.alphabet == SMS_ALPHABET_UCS2) { - uint16_t utf16_chr[3]; - char *ud = pdu_data.ud + (pdu_data.udhl ? 2 + 2 * pdu_data.udhl : 0); - char *out_buf_ptr = out_buf; - uint16_t *nchr; - int avail = out_buf_avail; + snprintk(at_cmd, sizeof(at_cmd), "AT+CMGF=0"); + ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, NULL, 0U, at_cmd, &mdata.sem_response, + MDM_CMD_LONG_RSP_TIME); + if (ret < 0) { + LOG_ERR("%s ret:%d", at_cmd, ret); + } - utf16_chr[2] = 0; - while (strlen(ud) && avail) { - memset(utf16_chr, 0, sizeof(utf16_chr)); - hex_str_to_data(ud, (uint8_t *)utf16_chr, 4); - utf16_chr[0] = sys_be16_to_cpu(utf16_chr[0]); - utf16_chr[1] = sys_be16_to_cpu(utf16_chr[1]); - ret = utf16le_to_utf8(utf16_chr, out_buf_ptr, avail, &nchr); - if (ret) { - if (pdu_data.udhl && first_msg) { - LOG_WRN("Buffer too small: partial " - "message copied"); - break; - } - out_buf = '\0'; - LOG_WRN("Buffer too small: unable to " - "concatenate part %d", - csms_idx); - return 0; - } - avail -= strlen(out_buf_ptr); - out_buf_ptr += strlen(out_buf_ptr); - if (nchr == &utf16_chr[1]) { - ud += 4; - } else { - ud += 8; - } - } - } else if (pdu_data.alphabet == SMS_ALPHABET_GSM7) { - if (!pdu_data.udhl) { - ret = gsm7_decode(pdu_data.ud, pdu_data.udl, out_buf, out_buf_avail, 0); - if (ret) { - LOG_WRN("Buffer too small: partial message " - "copied"); - } - } else { - uint8_t skip = ((pdu_data.udhl + 1) * 8 + 6) / 7; + k_sem_give(&mdata.sem_sms); - ret = gsm7_decode(pdu_data.ud, pdu_data.udl, out_buf, out_buf_avail, skip); - if (ret && !first_msg) { - LOG_WRN("Buffer too small: unable to " - "concatenate part %d", - csms_idx); - *out_buf = '\0'; - return 0; - } else if (ret) { - LOG_WRN("Buffer too small: partial message " - "copied"); - } - } - } - return 0; + return ret; } -int recv_sms_msg(void *obj, struct sms_in *sms) +/** + * @brief Receive an sms message + */ +static int recv_sms_msg(struct sms_in *sms) { - ARG_UNUSED(obj); int ret; if (!sms) { @@ -2698,7 +2809,7 @@ int recv_sms_msg(void *obj, struct sms_in *sms) k_sem_take(&mdata.sem_sms, K_FOREVER); ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, NULL, 0U, "AT+CMGF=0", - &mdata.sem_response, MDM_CMD_RSP_TIME); + &mdata.sem_response, MDM_CMD_LONG_RSP_TIME); mdata.sms = sms; k_sem_reset(&mdata.sem_rcv_sms); int count = 0; @@ -2784,6 +2895,34 @@ int recv_sms_msg(void *obj, struct sms_in *sms) return strlen(sms->msg); } +#if defined(CONFIG_MODEM_SMS_CALLBACK) +static int recv_sms_cb_en(bool enable) +{ + int ret = 0; + char *at_cmd; + + if (enable) { + at_cmd = "AT+CNMI=2,2,0,1,0"; + ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, NULL, 0, at_cmd, + &mdata.sem_response, MDM_CMD_RSP_TIME); + + if (ret < 0) { + LOG_ERR("%s ret:%d", at_cmd, ret); + } + } else { + at_cmd = "AT+CNMI=2,1,0,1,0"; + ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, NULL, 0, at_cmd, + &mdata.sem_response, MDM_CMD_RSP_TIME); + + if (ret < 0) { + LOG_ERR("%s ret:%d", at_cmd, ret); + } + } + return ret; +} +#endif /* CONFIG_MODEM_SMS_CALLBACK */ +#endif /* CONFIG_MODEM_SMS */ + /** * @brief Receive data on a socket */ @@ -4638,7 +4777,8 @@ static int murata_1sc_setup(void) SETUP_CMD_NOHANDLE("ATE0"), SETUP_CMD_NOHANDLE("ATV1"), SETUP_CMD_NOHANDLE("AT%CSDH=1"), - SETUP_CMD_NOHANDLE("AT+CNMI=2,1,2,1,0"), + SETUP_CMD_NOHANDLE("AT+CNMI=2,1,0,1,0"), + SETUP_CMD_NOHANDLE("AT+CMGF=0"), SETUP_CMD("AT+CGMI", "", on_cmd_get_manufacturer, 0U, ""), SETUP_CMD("AT+CGMM", "", on_cmd_get_model, 0U, ""), SETUP_CMD("AT+CGMR", "RK_", on_cmd_get_revision, 0U, ""), @@ -4810,15 +4950,17 @@ static int offload_ioctl(void *obj, unsigned int request, va_list args) return modem_socket_poll_update(obj, pfd, pev); } +#if defined(CONFIG_MODEM_SMS) case SMS_SEND: - ret = send_sms_msg(obj, (struct sms_out *)ptr_from_va(args)); + ret = send_sms_msg((struct sms_out *)ptr_from_va(args)); va_end(args); break; case SMS_RECV: - ret = recv_sms_msg(obj, (struct sms_in *)ptr_from_va(args)); + ret = recv_sms_msg((struct sms_in *)ptr_from_va(args)); va_end(args); break; +#endif /* CONFIG_MODEM_SMS */ case GET_IPV4_CONF: a_ipv4_addr = (struct aggr_ipv4_addr *)ptr_from_va(args); @@ -4983,8 +5125,6 @@ static int murata_1sc_init(const struct device *dev) { int ret = 0; - ARG_UNUSED(dev); - k_sem_init(&mdata.sem_response, 0, 1); k_sem_init(&mdata.sem_sock_conn, 0, 1); k_sem_init(&mdata.sem_xlate_buf, 1, 1); @@ -5020,10 +5160,17 @@ static int murata_1sc_init(const struct device *dev) mctx.data_revision = mdata.mdm_revision; mctx.data_imei = mdata.mdm_imei; +#if defined(CONFIG_MODEM_SMS) /* SMS functions */ mctx.send_sms = send_sms_msg; mctx.recv_sms = recv_sms_msg; +#if defined(CONFIG_MODEM_SMS_CALLBACK) + mctx.recv_sms_cb_en = recv_sms_cb_en; +#endif /* CONFIG_MODEM_SMS_CALLBACK */ +#endif /* CONFIG_MODEM_SMS */ + mctx.driver_data = &mdata; + mctx.dev = dev; /* pin setup */ ret = gpio_pin_configure_dt(&wake_host_gpio, GPIO_INPUT | GPIO_PULL_DOWN); diff --git a/include/zephyr/drivers/modem/sms.h b/include/zephyr/drivers/modem/sms.h new file mode 100644 index 00000000000..e51d711ca70 --- /dev/null +++ b/include/zephyr/drivers/modem/sms.h @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2023 T-Mobile USA, Inc. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** @file + * @brief Modem SMS for SMS common structure. + * + * Modem SMS handling for modem driver. + */ + +#ifndef ZEPHYR_INCLUDE_DRIVERS_MODEM_MODEM_SMS_H_ +#define ZEPHYR_INCLUDE_DRIVERS_MODEM_MODEM_SMS_H_ + +#include +#include + +#define SMS_PHONE_MAX_LEN 16 +#define SMS_TIME_MAX_LEN 26 + +/* + * Structure for an outgoing SMS message + * Note: All fields in sms_out and sms_in are NULL terminated + */ +struct sms_out { + /* Target phone number */ + char phone[SMS_PHONE_MAX_LEN]; + + /* Character array containing the message to be sent, NULL terminated */ + char msg[CONFIG_MODEM_SMS_OUT_MSG_MAX_LEN + 1]; +}; + +/* + * Structure for an incoming SMS message + * Note: All fields in sms_out and sms_in are NULL terminated + */ +struct sms_in { + + /* Message orignator number */ + char phone[SMS_PHONE_MAX_LEN]; + + /* SCTS Timestamp from the message */ + char time[SMS_TIME_MAX_LEN]; + + /* Character array containing the message received, NULL terminated */ + char msg[CONFIG_MODEM_SMS_IN_MSG_MAX_LEN + 1]; + + /* Concatenated SMS reference number */ + uint8_t csms_ref; + + /* Concatednamed SMS index number */ + uint8_t csms_idx; + + /* SMS Receive Timeout */ + k_timeout_t timeout; +}; + +/* Modem context SMS send function */ + +/** + * @brief Modem context SMS send function + * + * @param sms Pointer to an sms_out struct containing the message to be sent + * @return 0 on success, negative error value on failure + */ +typedef int (*send_sms_func_t)(const struct sms_out *sms); + +/** + * @brief Modem context SMS receive function + * + * @param sms Pointer to an sms_in struct to which the message shall be written + * @return 0 on success, negative error value on failure + */ +typedef int (*recv_sms_func_t)(struct sms_in *sms); + +/** + * @brief Modem context SMS receive callback enable function + * + * @param enable Enablement status: true to enable, false to disable + * @return 0 on success, negative error value on failure + */ +typedef int (*recv_sms_cb_en_func_t)(bool enable); + +/** + * Keeping this for compatibility, but this could likely be removed + */ +enum io_ctl { + SMS_SEND, + SMS_RECV, +}; + +/* + * SMS Receive callback structure. + */ +struct sms_recv_cb { + /** + * @brief A new SMS message has been received. + * + * This callback notifies the application of a new message. + * + * + * @param dev Device pointer to the modem which received the message + * @param sms Received SMS Message + * @param csms_ref CSMS Reference number (if available, value is set to -1 if not) + * @param csms_idx CSMS Index number (if available, value is set to 1 if not) + * @param csms_tot CSMS Total segment count (if available, value is set to 1 if not) + * + */ + void (*recv)(const struct device *dev, struct sms_in *sms, int csms_ref, int csms_idx, + int csms_tot); + + /** Internally used field for list handling */ + sys_snode_t node; +}; + +/** + * @brief Send SMS from a modem + * + * @param dev Device pointer for the modem from which the message shall be read + * @param sms Pointer to an sms_out struct containing the message to be sent + * @return 0 on success, negative error value on failure + */ +int sms_msg_send(const struct device *dev, const struct sms_out *sms); + +/** + * @brief Read SMS message from a modem + * + * @param dev Device pointer for the modem from which the message shall be read + * @param sms Pointer to an sms_in struct to which the message shall be written + * @return 0 on success, negative error value on failure + */ +int sms_msg_recv(const struct device *dev, struct sms_in *sms); + +/** + * @brief Enable SMS receive callbacks on a specified modem + * + * @param dev Device pointer for the modem on which to enable callbacks + * @param enable Enablement status: true to enable, false to disable + * @return 0 on success, negative error value on failure + */ +int sms_recv_cb_en(const struct device *dev, bool enable); + +/** + * @brief Register SMS receive callbacks. + * + * @param cb Callback struct. + * + * @return Zero on success or negative error code otherwise + */ +int sms_recv_cb_register(struct sms_recv_cb *cb); + +/** + * @brief Unregister SMS receive callbacks. + * + * @param cb Callback struct. + * + * @return Zero on success or negative error code otherwise + */ +int sms_recv_cb_unregister(struct sms_recv_cb *cb); + +#endif /* ZEPHYR_INCLUDE_DRIVERS_MODEM_MODEM_SMS_H_ */