LCOV - code coverage report
Current view: top level - zephyr/modem - chat.h Coverage Total Hit
Test: new.info Lines: 52.6 % 114 60
Test Date: 2025-09-05 20:47:19

            Line data    Source code
       1            0 : /*
       2              :  * Copyright (c) 2022 Trackunit Corporation
       3              :  *
       4              :  * SPDX-License-Identifier: Apache-2.0
       5              :  */
       6              : 
       7              : #include <zephyr/kernel.h>
       8              : #include <zephyr/types.h>
       9              : #include <zephyr/device.h>
      10              : #include <zephyr/sys/ring_buffer.h>
      11              : 
      12              : #include <zephyr/modem/pipe.h>
      13              : #include <zephyr/modem/stats.h>
      14              : 
      15              : #ifndef ZEPHYR_MODEM_CHAT_
      16              : #define ZEPHYR_MODEM_CHAT_
      17              : 
      18              : #ifdef __cplusplus
      19              : extern "C" {
      20              : #endif
      21              : 
      22              : /**
      23              :  * @brief Modem Chat
      24              :  * @defgroup modem_chat Modem Chat
      25              :  * @since 3.5
      26              :  * @version 1.0.0
      27              :  * @ingroup modem
      28              :  * @{
      29              :  */
      30              : 
      31              : struct modem_chat;
      32              : 
      33              : /**
      34              :  * @brief Callback called when matching chat is received
      35              :  *
      36              :  * @param chat Pointer to chat instance instance
      37              :  * @param argv Pointer to array of parsed arguments
      38              :  * @param argc Number of parsed arguments, arg 0 holds the exact match
      39              :  * @param user_data Free to use user data set during modem_chat_init()
      40              :  */
      41            1 : typedef void (*modem_chat_match_callback)(struct modem_chat *chat, char **argv, uint16_t argc,
      42              :                                           void *user_data);
      43              : 
      44              : /**
      45              :  * @brief Modem chat match
      46              :  */
      47            1 : struct modem_chat_match {
      48              :         /** Match array */
      49            1 :         const uint8_t *match;
      50              :         /** Size of match */
      51            1 :         uint8_t match_size;
      52              :         /** Separators array */
      53            1 :         const uint8_t *separators;
      54              :         /** Size of separators array */
      55            1 :         uint8_t separators_size;
      56              :         /** Set if modem chat instance shall use wildcards when matching */
      57            1 :         bool wildcards;
      58              :         /** Set if script shall not continue to next step in case of match */
      59            1 :         bool partial;
      60              :         /** Type of modem chat instance */
      61            1 :         modem_chat_match_callback callback;
      62              : };
      63              : 
      64            0 : #define MODEM_CHAT_MATCH(_match, _separators, _callback)                                           \
      65              :         {                                                                                          \
      66              :                 .match = (uint8_t *)(_match), .match_size = (uint8_t)(sizeof(_match) - 1),         \
      67              :                 .separators = (uint8_t *)(_separators),                                            \
      68              :                 .separators_size = (uint8_t)(sizeof(_separators) - 1), .wildcards = false,         \
      69              :                 .callback = _callback,                                                             \
      70              :         }
      71              : 
      72            0 : #define MODEM_CHAT_MATCH_WILDCARD(_match, _separators, _callback)                                  \
      73              :         {                                                                                          \
      74              :                 .match = (uint8_t *)(_match), .match_size = (uint8_t)(sizeof(_match) - 1),         \
      75              :                 .separators = (uint8_t *)(_separators),                                            \
      76              :                 .separators_size = (uint8_t)(sizeof(_separators) - 1), .wildcards = true,          \
      77              :                 .callback = _callback,                                                             \
      78              :         }
      79              : 
      80            0 : #define MODEM_CHAT_MATCH_INITIALIZER(_match, _separators, _callback, _wildcards, _partial)         \
      81              :         {                                                                                          \
      82              :                 .match = (uint8_t *)(_match),                                                      \
      83              :                 .match_size = (uint8_t)(sizeof(_match) - 1),                                       \
      84              :                 .separators = (uint8_t *)(_separators),                                            \
      85              :                 .separators_size = (uint8_t)(sizeof(_separators) - 1),                             \
      86              :                 .wildcards = _wildcards,                                                           \
      87              :                 .partial = _partial,                                                               \
      88              :                 .callback = _callback,                                                             \
      89              :         }
      90              : 
      91            0 : #define MODEM_CHAT_MATCH_DEFINE(_sym, _match, _separators, _callback)                              \
      92              :         const static struct modem_chat_match _sym = MODEM_CHAT_MATCH(_match, _separators, _callback)
      93              : 
      94            0 : #define MODEM_CHAT_MATCH_WILDCARD_DEFINE(_sym, _match, _separators, _callback)                     \
      95              :         const static struct modem_chat_match _sym =                                                \
      96              :                 MODEM_CHAT_MATCH_WILDCARD(_match, _separators, _callback)
      97              : 
      98              : /* Helper struct to match any response without callback. */
      99            0 : extern const struct modem_chat_match modem_chat_any_match;
     100              : 
     101            0 : #define MODEM_CHAT_MATCHES_DEFINE(_sym, ...)                                                       \
     102              :         const static struct modem_chat_match _sym[] = {__VA_ARGS__}
     103              : 
     104              : /* Helper struct to match nothing. */
     105            0 : extern const struct modem_chat_match modem_chat_empty_matches[0];
     106              : 
     107              : /**
     108              :  * @brief Modem chat script chat
     109              :  */
     110            1 : struct modem_chat_script_chat {
     111              :         /** Request to send to modem */
     112            1 :         const uint8_t *request;
     113              :         /** Size of request */
     114            1 :         uint16_t request_size;
     115              :         /** Expected responses to request */
     116            1 :         const struct modem_chat_match *response_matches;
     117              :         /** Number of elements in expected responses */
     118            1 :         uint16_t response_matches_size;
     119              :         /** Timeout before chat script may continue to next step in milliseconds */
     120            1 :         uint16_t timeout;
     121              : };
     122              : 
     123            0 : #define MODEM_CHAT_SCRIPT_CMD_RESP(_request, _response_match)                                      \
     124              :         {                                                                                          \
     125              :                 .request = (uint8_t *)(_request),                                                  \
     126              :                 .request_size = (uint16_t)(sizeof(_request) - 1),                                  \
     127              :                 .response_matches = &_response_match,                                              \
     128              :                 .response_matches_size = 1,                                                        \
     129              :                 .timeout = 0,                                                                      \
     130              :         }
     131              : 
     132            0 : #define MODEM_CHAT_SCRIPT_CMD_RESP_MULT(_request, _response_matches)                               \
     133              :         {                                                                                          \
     134              :                 .request = (uint8_t *)(_request),                                                  \
     135              :                 .request_size = (uint16_t)(sizeof(_request) - 1),                                  \
     136              :                 .response_matches = _response_matches,                                             \
     137              :                 .response_matches_size = ARRAY_SIZE(_response_matches),                            \
     138              :                 .timeout = 0,                                                                      \
     139              :         }
     140              : 
     141            0 : #define MODEM_CHAT_SCRIPT_CMD_RESP_NONE(_request, _timeout_ms)                                     \
     142              :         {                                                                                          \
     143              :                 .request = (uint8_t *)(_request),                                                  \
     144              :                 .request_size = (uint16_t)(sizeof(_request) - 1),                                  \
     145              :                 .response_matches = NULL,                                                          \
     146              :                 .response_matches_size = 0,                                                        \
     147              :                 .timeout = _timeout_ms,                                                            \
     148              :         }
     149              : 
     150            0 : #define MODEM_CHAT_SCRIPT_CMDS_DEFINE(_sym, ...)                                                   \
     151              :         const static struct modem_chat_script_chat _sym[] = {__VA_ARGS__}
     152              : 
     153              : /* Helper struct to have no chat script command. */
     154            0 : extern const struct modem_chat_script_chat modem_chat_empty_script_chats[0];
     155              : 
     156            0 : enum modem_chat_script_result {
     157              :         MODEM_CHAT_SCRIPT_RESULT_SUCCESS,
     158              :         MODEM_CHAT_SCRIPT_RESULT_ABORT,
     159              :         MODEM_CHAT_SCRIPT_RESULT_TIMEOUT
     160              : };
     161              : 
     162              : /**
     163              :  * @brief Callback called when script chat is received
     164              :  *
     165              :  * @param chat Pointer to chat instance instance
     166              :  * @param result Result of script execution
     167              :  * @param user_data Free to use user data set during modem_chat_init()
     168              :  */
     169            1 : typedef void (*modem_chat_script_callback)(struct modem_chat *chat,
     170              :                                            enum modem_chat_script_result result, void *user_data);
     171              : 
     172              : /**
     173              :  * @brief Modem chat script
     174              :  */
     175            1 : struct modem_chat_script {
     176              :         /** Name of script */
     177            1 :         const char *name;
     178              :         /** Array of script chats */
     179            1 :         const struct modem_chat_script_chat *script_chats;
     180              :         /** Elements in array of script chats */
     181            1 :         uint16_t script_chats_size;
     182              :         /** Array of abort matches */
     183            1 :         const struct modem_chat_match *abort_matches;
     184              :         /** Number of elements in array of abort matches */
     185            1 :         uint16_t abort_matches_size;
     186              :         /** Callback called when script execution terminates */
     187            1 :         modem_chat_script_callback callback;
     188              :         /** Timeout in seconds within which the script execution must terminate */
     189            1 :         uint32_t timeout;
     190              : };
     191              : 
     192            0 : #define MODEM_CHAT_SCRIPT_DEFINE(_sym, _script_chats, _abort_matches, _callback, _timeout_s)       \
     193              :         const static struct modem_chat_script _sym = {                                             \
     194              :                 .name = #_sym,                                                                     \
     195              :                 .script_chats = _script_chats,                                                     \
     196              :                 .script_chats_size = ARRAY_SIZE(_script_chats),                                    \
     197              :                 .abort_matches = _abort_matches,                                                   \
     198              :                 .abort_matches_size = ARRAY_SIZE(_abort_matches),                                  \
     199              :                 .callback = _callback,                                                             \
     200              :                 .timeout = _timeout_s,                                                             \
     201              :         }
     202              : 
     203            0 : #define MODEM_CHAT_SCRIPT_NO_ABORT_DEFINE(_sym, _script_chats, _callback, _timeout_s)              \
     204              :         MODEM_CHAT_SCRIPT_DEFINE(_sym, _script_chats, modem_chat_empty_matches,                    \
     205              :                                  _callback, _timeout_s)
     206              : 
     207            0 : #define MODEM_CHAT_SCRIPT_EMPTY_DEFINE(_sym)                                                       \
     208              :         MODEM_CHAT_SCRIPT_NO_ABORT_DEFINE(_sym, modem_chat_empty_script_chats, NULL, 0)
     209              : 
     210            0 : enum modem_chat_script_send_state {
     211              :         /* No data to send */
     212              :         MODEM_CHAT_SCRIPT_SEND_STATE_IDLE,
     213              :         /* Sending request */
     214              :         MODEM_CHAT_SCRIPT_SEND_STATE_REQUEST,
     215              :         /* Sending delimiter */
     216              :         MODEM_CHAT_SCRIPT_SEND_STATE_DELIMITER,
     217              : };
     218              : 
     219              : /**
     220              :  * @brief Chat instance internal context
     221              :  * @warning Do not modify any members of this struct directly
     222              :  */
     223            1 : struct modem_chat {
     224              :         /* Pipe used to send and receive data */
     225            0 :         struct modem_pipe *pipe;
     226              : 
     227              :         /* User data passed with match callbacks */
     228            0 :         void *user_data;
     229              : 
     230              :         /* Receive buffer */
     231            0 :         uint8_t *receive_buf;
     232            0 :         uint16_t receive_buf_size;
     233            0 :         uint16_t receive_buf_len;
     234              : 
     235              :         /* Work buffer */
     236            0 :         uint8_t work_buf[32];
     237            0 :         uint16_t work_buf_len;
     238              : 
     239              :         /* Chat delimiter */
     240            0 :         uint8_t *delimiter;
     241            0 :         uint16_t delimiter_size;
     242            0 :         uint16_t delimiter_match_len;
     243              : 
     244              :         /* Array of bytes which are discarded out by parser */
     245            0 :         uint8_t *filter;
     246            0 :         uint16_t filter_size;
     247              : 
     248              :         /* Parsed arguments */
     249            0 :         uint8_t **argv;
     250            0 :         uint16_t argv_size;
     251            0 :         uint16_t argc;
     252              : 
     253              :         /* Matches
     254              :          * Index 0 -> Response matches
     255              :          * Index 1 -> Abort matches
     256              :          * Index 2 -> Unsolicited matches
     257              :          */
     258            0 :         const struct modem_chat_match *matches[3];
     259            0 :         uint16_t matches_size[3];
     260              : 
     261              :         /* Script execution */
     262            0 :         const struct modem_chat_script *script;
     263            0 :         const struct modem_chat_script *pending_script;
     264            0 :         struct k_work script_run_work;
     265            0 :         struct k_work_delayable script_timeout_work;
     266            0 :         struct k_work script_abort_work;
     267            0 :         uint16_t script_chat_it;
     268            0 :         atomic_t script_state;
     269            0 :         enum modem_chat_script_result script_result;
     270            0 :         struct k_sem script_stopped_sem;
     271              : 
     272              :         /* Script sending */
     273            0 :         enum modem_chat_script_send_state script_send_state;
     274            0 :         uint16_t script_send_pos;
     275            0 :         struct k_work script_send_work;
     276            0 :         struct k_work_delayable script_send_timeout_work;
     277              : 
     278              :         /* Match parsing */
     279            0 :         const struct modem_chat_match *parse_match;
     280            0 :         uint16_t parse_match_len;
     281            0 :         uint16_t parse_arg_len;
     282            0 :         uint16_t parse_match_type;
     283              : 
     284              :         /* Process received data */
     285            0 :         struct k_work receive_work;
     286              : 
     287              :         /* Statistics */
     288              : #if CONFIG_MODEM_STATS
     289              :         struct modem_stats_buffer receive_buf_stats;
     290              :         struct modem_stats_buffer work_buf_stats;
     291              : #endif
     292              : };
     293              : 
     294              : /**
     295              :  * @brief Chat configuration
     296              :  */
     297            1 : struct modem_chat_config {
     298              :         /** Free to use user data passed with modem match callbacks */
     299            1 :         void *user_data;
     300              :         /** Receive buffer used to store parsed arguments */
     301            1 :         uint8_t *receive_buf;
     302              :         /** Size of receive buffer should be longest line + longest match */
     303            1 :         uint16_t receive_buf_size;
     304              :         /** Delimiter */
     305            1 :         uint8_t *delimiter;
     306              :         /** Size of delimiter */
     307            1 :         uint8_t delimiter_size;
     308              :         /** Bytes which are discarded by parser */
     309            1 :         uint8_t *filter;
     310              :         /** Size of filter */
     311            1 :         uint8_t filter_size;
     312              :         /** Array of pointers used to point to parsed arguments */
     313            1 :         uint8_t **argv;
     314              :         /** Elements in array of pointers */
     315            1 :         uint16_t argv_size;
     316              :         /** Array of unsolicited matches */
     317            1 :         const struct modem_chat_match *unsol_matches;
     318              :         /** Elements in array of unsolicited matches */
     319            1 :         uint16_t unsol_matches_size;
     320              : };
     321              : 
     322              : /**
     323              :  * @brief Initialize modem pipe chat instance
     324              :  * @param chat Chat instance
     325              :  * @param config Configuration which shall be applied to Chat instance
     326              :  * @note Chat instance must be attached to pipe
     327              :  */
     328            1 : int modem_chat_init(struct modem_chat *chat, const struct modem_chat_config *config);
     329              : 
     330              : /**
     331              :  * @brief Attach modem chat instance to pipe
     332              :  * @param chat Chat instance
     333              :  * @param pipe Pipe instance to attach Chat instance to
     334              :  * @returns 0 if successful
     335              :  * @returns negative errno code if failure
     336              :  * @note Chat instance is enabled if successful
     337              :  */
     338            1 : int modem_chat_attach(struct modem_chat *chat, struct modem_pipe *pipe);
     339              : 
     340              : /**
     341              :  * @brief Run script asynchronously
     342              :  * @param chat Chat instance
     343              :  * @param script Script to run
     344              :  * @returns 0 if script successfully started
     345              :  * @returns -EBUSY if a script is currently running
     346              :  * @returns -EPERM if modem pipe is not attached
     347              :  * @returns -EINVAL if arguments or script is invalid
     348              :  * @note Script runs asynchronously until complete or aborted.
     349              :  */
     350            1 : int modem_chat_run_script_async(struct modem_chat *chat, const struct modem_chat_script *script);
     351              : 
     352              : /**
     353              :  * @brief Run script
     354              :  * @param chat Chat instance
     355              :  * @param script Script to run
     356              :  * @returns 0 if successful
     357              :  * @returns -EBUSY if a script is currently running
     358              :  * @returns -EPERM if modem pipe is not attached
     359              :  * @returns -EINVAL if arguments or script is invalid
     360              :  * @note Script runs until complete or aborted.
     361              :  */
     362            1 : int modem_chat_run_script(struct modem_chat *chat, const struct modem_chat_script *script);
     363              : 
     364              : /**
     365              :  * @brief Run script asynchronously
     366              :  * @note Function exists for backwards compatibility and should be deprecated
     367              :  * @param chat Chat instance
     368              :  * @param script Script to run
     369              :  * @returns 0 if script successfully started
     370              :  * @returns -EBUSY if a script is currently running
     371              :  * @returns -EPERM if modem pipe is not attached
     372              :  * @returns -EINVAL if arguments or script is invalid
     373              :  */
     374            1 : static inline int modem_chat_script_run(struct modem_chat *chat,
     375              :                                         const struct modem_chat_script *script)
     376              : {
     377              :         return modem_chat_run_script_async(chat, script);
     378              : }
     379              : 
     380              : /**
     381              :  * @brief Abort script
     382              :  * @param chat Chat instance
     383              :  */
     384            1 : void modem_chat_script_abort(struct modem_chat *chat);
     385              : 
     386              : /**
     387              :  * @brief Release pipe from chat instance
     388              :  * @param chat Chat instance
     389              :  */
     390            1 : void modem_chat_release(struct modem_chat *chat);
     391              : 
     392              : /**
     393              :  * @brief Initialize modem chat match
     394              :  * @param chat_match Modem chat match instance
     395              :  */
     396            1 : void modem_chat_match_init(struct modem_chat_match *chat_match);
     397              : 
     398              : /**
     399              :  * @brief Set match of modem chat match instance
     400              :  * @param chat_match Modem chat match instance
     401              :  * @param match Match to set
     402              :  * @note The lifetime of match must match or exceed the lifetime of chat_match
     403              :  * @warning Always call this API after match is modified
     404              :  *
     405              :  * @retval 0 if successful, negative errno code otherwise
     406              :  */
     407            1 : int modem_chat_match_set_match(struct modem_chat_match *chat_match, const char *match);
     408              : 
     409              : /**
     410              :  * @brief Set separators of modem chat match instance
     411              :  * @param chat_match Modem chat match instance
     412              :  * @param separators Separators to set
     413              :  * @note The lifetime of separators must match or exceed the lifetime of chat_match
     414              :  * @warning Always call this API after separators are modified
     415              :  *
     416              :  * @retval 0 if successful, negative errno code otherwise
     417              :  */
     418            1 : int modem_chat_match_set_separators(struct modem_chat_match *chat_match, const char *separators);
     419              : 
     420              : /**
     421              :  * @brief Set modem chat match callback
     422              :  * @param chat_match Modem chat match instance
     423              :  * @param callback Callback to set
     424              :  */
     425            1 : void modem_chat_match_set_callback(struct modem_chat_match *chat_match,
     426              :                                    modem_chat_match_callback callback);
     427              : 
     428              : /**
     429              :  * @brief Set modem chat match partial flag
     430              :  * @param chat_match Modem chat match instance
     431              :  * @param partial Partial flag to set
     432              :  */
     433            1 : void modem_chat_match_set_partial(struct modem_chat_match *chat_match, bool partial);
     434              : 
     435              : /**
     436              :  * @brief Set modem chat match wildcards flag
     437              :  * @param chat_match Modem chat match instance
     438              :  * @param enable Enable/disable Wildcards
     439              :  */
     440            1 : void modem_chat_match_enable_wildcards(struct modem_chat_match *chat_match, bool enable);
     441              : 
     442              : /**
     443              :  * @brief Initialize modem chat script chat
     444              :  * @param script_chat Modem chat script chat instance
     445              :  */
     446            1 : void modem_chat_script_chat_init(struct modem_chat_script_chat *script_chat);
     447              : 
     448              : /**
     449              :  * @brief Set request of modem chat script chat instance
     450              :  * @param script_chat Modem chat script chat instance
     451              :  * @param request Request to set
     452              :  * @note The lifetime of request must match or exceed the lifetime of script_chat
     453              :  * @warning Always call this API after request is modified
     454              :  *
     455              :  * @retval 0 if successful, negative errno code otherwise
     456              :  */
     457            1 : int modem_chat_script_chat_set_request(struct modem_chat_script_chat *script_chat,
     458              :                                        const char *request);
     459              : 
     460              : /**
     461              :  * @brief Set modem chat script chat matches
     462              :  * @param script_chat Modem chat script chat instance
     463              :  * @param response_matches Response match array to set
     464              :  * @param response_matches_size Size of response match array
     465              :  * @note The lifetime of response_matches must match or exceed the lifetime of script_chat
     466              :  *
     467              :  * @retval 0 if successful, negative errno code otherwise
     468              :  */
     469            1 : int modem_chat_script_chat_set_response_matches(struct modem_chat_script_chat *script_chat,
     470              :                                                 const struct modem_chat_match *response_matches,
     471              :                                                 uint16_t response_matches_size);
     472              : 
     473              : /**
     474              :  * @brief Set modem chat script chat timeout
     475              :  * @param script_chat Modem chat script chat instance
     476              :  * @param timeout_ms Timeout in milliseconds
     477              :  */
     478            1 : void modem_chat_script_chat_set_timeout(struct modem_chat_script_chat *script_chat,
     479              :                                         uint16_t timeout_ms);
     480              : 
     481              : /**
     482              :  * @brief Initialize modem chat script
     483              :  * @param script Modem chat script instance
     484              :  */
     485            1 : void modem_chat_script_init(struct modem_chat_script *script);
     486              : 
     487              : /**
     488              :  * @brief Set modem chat script name
     489              :  * @param script Modem chat script instance
     490              :  * @param name Name to set
     491              :  * @note The lifetime of name must match or exceed the lifetime of script
     492              :  */
     493            1 : void modem_chat_script_set_name(struct modem_chat_script *script, const char *name);
     494              : 
     495              : /**
     496              :  * @brief Set modem chat script chats
     497              :  * @param script Modem chat script instance
     498              :  * @param script_chats Chat script array to set
     499              :  * @param script_chats_size Size of chat script array
     500              :  * @note The lifetime of script_chats must match or exceed the lifetime of script
     501              :  *
     502              :  * @retval 0 if successful, negative errno code otherwise
     503              :  */
     504            1 : int modem_chat_script_set_script_chats(struct modem_chat_script *script,
     505              :                                        const struct modem_chat_script_chat *script_chats,
     506              :                                        uint16_t script_chats_size);
     507              : 
     508              : /**
     509              :  * @brief Set modem chat script abort matches
     510              :  * @param script Modem chat script instance
     511              :  * @param abort_matches Abort match array to set
     512              :  * @param abort_matches_size Size of abort match array
     513              :  * @note The lifetime of abort_matches must match or exceed the lifetime of script
     514              :  *
     515              :  * @retval 0 if successful, negative errno code otherwise
     516              :  */
     517            1 : int modem_chat_script_set_abort_matches(struct modem_chat_script *script,
     518              :                                         const struct modem_chat_match *abort_matches,
     519              :                                         uint16_t abort_matches_size);
     520              : 
     521              : /**
     522              :  * @brief Set modem chat script callback
     523              :  * @param script Modem chat script instance
     524              :  * @param callback Callback to set
     525              :  */
     526            1 : void modem_chat_script_set_callback(struct modem_chat_script *script,
     527              :                                     modem_chat_script_callback callback);
     528              : 
     529              : /**
     530              :  * @brief Set modem chat script timeout
     531              :  * @param script Modem chat script instance
     532              :  * @param timeout_s Timeout in seconds
     533              :  */
     534            1 : void modem_chat_script_set_timeout(struct modem_chat_script *script, uint32_t timeout_s);
     535              : 
     536              : /**
     537              :  * @}
     538              :  */
     539              : 
     540              : #ifdef __cplusplus
     541              : }
     542              : #endif
     543              : 
     544              : #endif /* ZEPHYR_MODEM_CHAT_ */
        

Generated by: LCOV version 2.0-1