diff options
author | Tambet Ingo <tambet@gmail.com> | 2008-09-11 08:35:32 +0300 |
---|---|---|
committer | Tambet Ingo <tambet@gmail.com> | 2008-09-11 08:35:32 +0300 |
commit | ac4409e7cea29e03d311e6b805a084837d8bb70f (patch) | |
tree | 6b534ff91a3976a0268a89c858543bc973b17f8c /src/mm-serial-parsers.c | |
parent | bb874acea0c8552f86932084e222b45a94119f29 (diff) |
Rewrite serial device communications.
Instead of vague "send something, wait something" the responses are now
analyzed by (overridable) parsers. Makes all the modem implementations much
easier since each caller knows without any code whether the call succeeded
or failed.
Another thing that makes modem code simpler (and the whole thing more robust),
is the queueing of sent commands. Each queued command has a command and a
callback which is quaranteed to get called, even if sending failed.
Define and implement error reporting.
Diffstat (limited to 'src/mm-serial-parsers.c')
-rw-r--r-- | src/mm-serial-parsers.c | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/src/mm-serial-parsers.c b/src/mm-serial-parsers.c new file mode 100644 index 00000000..cc23cae8 --- /dev/null +++ b/src/mm-serial-parsers.c @@ -0,0 +1,266 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ + +#include <string.h> +#include <stdlib.h> + +#include "mm-serial-parsers.h" +#include "mm-errors.h" + +/* Clean up the response by removing control characters like <CR><LF> etc */ +static void +response_clean (GString *response) +{ + char *s; + + /* Ends with '<CR><LF>' */ + s = response->str + response->len - 1; + if (*s == '\n' && *(--s) == '\r') + g_string_truncate (response, response->len - 2); + + /* Starts with '<CR><LF>' */ + s = response->str; + if (*s == '\r' && *(++s) == '\n') + g_string_erase (response, 0, 2); +} + + +static gboolean +remove_eval_cb (const GMatchInfo *match_info, + GString *result, + gpointer user_data) +{ + int *result_len = (int *) user_data; + int start; + int end; + + if (g_match_info_fetch_pos (match_info, 0, &start, &end)) + *result_len -= (end - start); + + return TRUE; +} + +static void +remove_matches (GRegex *r, GString *string) +{ + char *str; + int result_len = string->len; + + str = g_regex_replace_eval (r, string->str, string->len, 0, 0, + remove_eval_cb, &result_len, NULL); + + g_string_truncate (string, 0); + g_string_append_len (string, str, result_len); + g_free (str); +} + +/* FIXME: V0 parser is not finished */ +#if 0 +typedef struct { + GRegex *generic_response; + GRegex *detailed_error; +} MMSerialParserV0; + +gpointer +mm_serial_parser_v0_new (void) +{ + MMSerialParserV0 *parser; + GRegexCompileFlags flags = G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW | G_REGEX_OPTIMIZE; + + parser = g_slice_new (MMSerialParserV0); + + parser->generic_response = g_regex_new ("(\\d)\\r%", flags, 0, NULL); + parser->detailed_error = g_regex_new ("+CME ERROR: (\\d+)\\r\\n$", flags, 0, NULL); + + return parser; +} + +gboolean +mm_serial_parser_v0_parse (gpointer parser, + GString *response, + GError **error) +{ + MMSerialParserV0 *parser = (MMSerialParserV0 *) data; + GMatchInfo *match_info; + char *str; + int code; + gboolean found; + + found = g_regex_match_full (parser->generic_response, response->str, response->len, 0, 0, &match_info, NULL); + if (found) { + str = g_match_info_fetch (match_info, 1); + if (str) { + code = atoi (str); + g_free (str); + } + + g_match_info_free (match_info); + + return TRUE; + } + + found = g_regex_match_full (parser->detailed_error, response->str, response->len, 0, 0, &match_info, NULL); + if (found) { + str = g_match_info_fetch (match_info, 1); + if (str) { + code = atoi (str); + g_free (str); + } else + code = MM_MOBILE_ERROR_UNKNOWN; + + g_match_info_free (match_info); + + g_debug ("Got error code %d: %s", code, msg); + g_set_error (error, MM_MOBILE_ERROR, code, "%s", msg); + + return TRUE; + } + + return FALSE; +} + +void +mm_serial_parser_v0_destroy (gpointer parser) +{ + MMSerialParserV0 *parser = (MMSerialParserV0 *) data; + + g_regex_unref (parser->generic_response); + g_regex_unref (parser->detailed_error); + + g_slice_free (MMSerialParserV0, data); +} +#endif + +typedef struct { + GRegex *regex_ok; + GRegex *regex_connect; + GRegex *regex_detailed_error; + GRegex *regex_unknown_error; + GRegex *regex_connect_failed; +} MMSerialParserV1; + +gpointer +mm_serial_parser_v1_new (void) +{ + MMSerialParserV1 *parser; + GRegexCompileFlags flags = G_REGEX_DOLLAR_ENDONLY | G_REGEX_RAW | G_REGEX_OPTIMIZE; + + parser = g_slice_new (MMSerialParserV1); + + parser->regex_ok = g_regex_new ("\\r\\nOK\\r\\n$", flags, 0, NULL); + parser->regex_connect = g_regex_new ("\\r\\nCONNECT\\s*\\d*\\r\\n$", flags, 0, NULL); + parser->regex_detailed_error = g_regex_new ("\\r\\n\\+CME ERROR: (\\d+)\\r\\n$", flags, 0, NULL); + parser->regex_unknown_error = g_regex_new ("\\r\\n(ERROR)|(COMMAND NOT SUPPORT)\\r\\n$", flags, 0, NULL); + parser->regex_connect_failed = g_regex_new ("\\r\\n(NO CARRIER)|(BUSY)|(NO ANSWER)|(NO DIALTONE)\\r\\n$", flags, 0, NULL); + + return parser; +} + +gboolean +mm_serial_parser_v1_parse (gpointer data, + GString *response, + GError **error) +{ + MMSerialParserV1 *parser = (MMSerialParserV1 *) data; + GMatchInfo *match_info; + const char *msg; + int code; + gboolean found = FALSE; + + /* First, check for successfule responses */ + + found = g_regex_match_full (parser->regex_ok, response->str, response->len, 0, 0, NULL, NULL); + if (found) + remove_matches (parser->regex_ok, response); + else + found = g_regex_match_full (parser->regex_connect, response->str, response->len, 0, 0, NULL, NULL); + + if (found) { + response_clean (response); + return TRUE; + } + + /* Now failures */ + code = MM_MOBILE_ERROR_UNKNOWN; + + found = g_regex_match_full (parser->regex_detailed_error, + response->str, response->len, + 0, 0, &match_info, NULL); + + if (found) { + char *str; + + str = g_match_info_fetch (match_info, 1); + if (str) { + code = atoi (str); + g_free (str); + } + g_match_info_free (match_info); + } else + found = g_regex_match_full (parser->regex_unknown_error, response->str, response->len, 0, 0, NULL, NULL); + + if (found) { +#if 0 + /* FIXME: This does not work for some reason. */ + GEnumValue *enum_value; + + enum_value = g_enum_get_value ((GEnumClass *) g_type_class_peek_static (), code); + msg = enum_value->value_nick; +#endif + msg = "FIXME"; + + g_debug ("Got error code %d: %s", code, msg); + g_set_error (error, MM_MOBILE_ERROR, code, "%s", msg); + } else { + found = g_regex_match_full (parser->regex_connect_failed, + response->str, response->len, + 0, 0, &match_info, NULL); + if (found) { + char *str; + + str = g_match_info_fetch (match_info, 1); + if (str) { + if (!strcmp (str, "NO CARRIER")) { + code = MM_MODEM_CONNECT_ERROR_NO_CARRIER; + msg = "No carrier"; + } else if (!strcmp (str, "BUSY")) { + code = MM_MODEM_CONNECT_ERROR_BUSY; + msg = "Busy"; + } else if (!strcmp (str, "NO ANSWER")) { + code = MM_MODEM_CONNECT_ERROR_NO_ANSWER; + msg = "No answer"; + } else if (!strcmp (str, "NO DIALTONE")) { + code = MM_MODEM_CONNECT_ERROR_NO_DIALTONE; + msg = "No dialtone"; + } else { + g_warning ("Got matching connect failure, but can't parse it"); + /* uhm... make something up (yes, ok, lie!). */ + code = MM_MODEM_CONNECT_ERROR_NO_CARRIER; + msg = "No carrier"; + } + + g_free (str); + } + g_match_info_free (match_info); + + g_debug ("Got connect failure code %d: %s", code, msg); + g_set_error (error, MM_MODEM_CONNECT_ERROR, code, "%s", msg); + } + } + + return found; + +} + +void +mm_serial_parser_v1_destroy (gpointer data) +{ + MMSerialParserV1 *parser = (MMSerialParserV1 *) data; + + g_regex_unref (parser->regex_ok); + g_regex_unref (parser->regex_connect); + g_regex_unref (parser->regex_detailed_error); + g_regex_unref (parser->regex_unknown_error); + g_regex_unref (parser->regex_connect_failed); + + g_slice_free (MMSerialParserV1, data); +} |