modemmanager: add flow control patch series
https://jira.digi.com/browse/DEL-3841 Signed-off-by: Javier Viguera <javier.viguera@digi.com>
This commit is contained in:
parent
f67e7e949b
commit
a7c51af5b8
|
|
@ -0,0 +1,290 @@
|
||||||
|
From: Aleksander Morgado <aleksander@aleksander.es>
|
||||||
|
Date: Sat, 25 Mar 2017 00:47:50 +0100
|
||||||
|
Subject: [PATCH] =?UTF-8?q?helpers:=20new=20parser=20for=20AT+IFC=3D=3F?=
|
||||||
|
|
||||||
|
Instead of having the parser return separate list of supported flow
|
||||||
|
controls for TE and TA, we simplify it by only returning those
|
||||||
|
settings that apply to both TE and TA.
|
||||||
|
|
||||||
|
This logic isn't perfect either, though, as some settings (e.g. '3' in
|
||||||
|
TE in some modems, specifying a different XON/XOFF behavior) may not
|
||||||
|
have a corresponding setting in the other end.
|
||||||
|
|
||||||
|
The most common cases we care about (i.e. standard XON/XOFF, RTS/CTS)
|
||||||
|
should be properly reported with this logic.
|
||||||
|
---
|
||||||
|
src/mm-modem-helpers.c | 113 +++++++++++++++++++++++++++++++++++++++++
|
||||||
|
src/mm-modem-helpers.h | 17 +++++++
|
||||||
|
src/tests/test-modem-helpers.c | 100 ++++++++++++++++++++++++++++++++++++
|
||||||
|
3 files changed, 230 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/src/mm-modem-helpers.c b/src/mm-modem-helpers.c
|
||||||
|
index b45561768077..679f925281a3 100644
|
||||||
|
--- a/src/mm-modem-helpers.c
|
||||||
|
+++ b/src/mm-modem-helpers.c
|
||||||
|
@@ -525,6 +525,119 @@ mm_voice_clip_regex_get (void)
|
||||||
|
|
||||||
|
/*************************************************************************/
|
||||||
|
|
||||||
|
+static MMFlowControl
|
||||||
|
+flow_control_array_to_mask (GArray *array,
|
||||||
|
+ const gchar *item)
|
||||||
|
+{
|
||||||
|
+ MMFlowControl mask = MM_FLOW_CONTROL_UNKNOWN;
|
||||||
|
+ guint i;
|
||||||
|
+
|
||||||
|
+ for (i = 0; i < array->len; i++) {
|
||||||
|
+ guint mode;
|
||||||
|
+
|
||||||
|
+ mode = g_array_index (array, guint, i);
|
||||||
|
+ switch (mode) {
|
||||||
|
+ case 0:
|
||||||
|
+ mm_dbg ("%s supports no flow control", item);
|
||||||
|
+ mask |= MM_FLOW_CONTROL_NONE;
|
||||||
|
+ break;
|
||||||
|
+ case 1:
|
||||||
|
+ mm_dbg ("%s supports XON/XOFF flow control", item);
|
||||||
|
+ mask |= MM_FLOW_CONTROL_XON_XOFF;
|
||||||
|
+ break;
|
||||||
|
+ case 2:
|
||||||
|
+ mm_dbg ("%s supports RTS/CTS flow control", item);
|
||||||
|
+ mask |= MM_FLOW_CONTROL_RTS_CTS;
|
||||||
|
+ break;
|
||||||
|
+ default:
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return mask;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static MMFlowControl
|
||||||
|
+flow_control_match_info_to_mask (GMatchInfo *match_info,
|
||||||
|
+ guint index,
|
||||||
|
+ const gchar *item,
|
||||||
|
+ GError **error)
|
||||||
|
+{
|
||||||
|
+ MMFlowControl mask = MM_FLOW_CONTROL_UNKNOWN;
|
||||||
|
+ gchar *aux = NULL;
|
||||||
|
+ GArray *array = NULL;
|
||||||
|
+
|
||||||
|
+ if (!(aux = mm_get_string_unquoted_from_match_info (match_info, index))) {
|
||||||
|
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
||||||
|
+ "Error retrieving list of supported %s flow control methods", item);
|
||||||
|
+ goto out;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (!(array = mm_parse_uint_list (aux, error))) {
|
||||||
|
+ g_prefix_error (error, "Error parsing list of supported %s flow control methods: ", item);
|
||||||
|
+ goto out;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if ((mask = flow_control_array_to_mask (array, item)) == MM_FLOW_CONTROL_UNKNOWN) {
|
||||||
|
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
||||||
|
+ "No known %s flow control method given", item);
|
||||||
|
+ goto out;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+out:
|
||||||
|
+ g_clear_pointer (&aux, g_free);
|
||||||
|
+ g_clear_pointer (&array, g_array_unref);
|
||||||
|
+
|
||||||
|
+ return mask;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+MMFlowControl
|
||||||
|
+mm_parse_ifc_test_response (const gchar *response,
|
||||||
|
+ GError **error)
|
||||||
|
+{
|
||||||
|
+ GRegex *r;
|
||||||
|
+ GError *inner_error = NULL;
|
||||||
|
+ GMatchInfo *match_info = NULL;
|
||||||
|
+ MMFlowControl te_mask = MM_FLOW_CONTROL_UNKNOWN;
|
||||||
|
+ MMFlowControl ta_mask = MM_FLOW_CONTROL_UNKNOWN;
|
||||||
|
+ MMFlowControl mask = MM_FLOW_CONTROL_UNKNOWN;
|
||||||
|
+
|
||||||
|
+ r = g_regex_new ("(?:\\+IFC:)?\\s*\\((.*)\\),\\((.*)\\)(?:\\r\\n)?", 0, 0, NULL);
|
||||||
|
+ g_assert (r != NULL);
|
||||||
|
+
|
||||||
|
+ g_regex_match_full (r, response, strlen (response), 0, 0, &match_info, &inner_error);
|
||||||
|
+ if (inner_error)
|
||||||
|
+ goto out;
|
||||||
|
+
|
||||||
|
+ if (!g_match_info_matches (match_info)) {
|
||||||
|
+ inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't match response");
|
||||||
|
+ goto out;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /* Parse TE flow control methods */
|
||||||
|
+ if ((te_mask = flow_control_match_info_to_mask (match_info, 1, "TE", &inner_error)) == MM_FLOW_CONTROL_UNKNOWN)
|
||||||
|
+ goto out;
|
||||||
|
+
|
||||||
|
+ /* Parse TA flow control methods */
|
||||||
|
+ if ((ta_mask = flow_control_match_info_to_mask (match_info, 2, "TA", &inner_error)) == MM_FLOW_CONTROL_UNKNOWN)
|
||||||
|
+ goto out;
|
||||||
|
+
|
||||||
|
+ /* Only those methods in both TA and TE will be the ones we report */
|
||||||
|
+ mask = te_mask & ta_mask;
|
||||||
|
+
|
||||||
|
+out:
|
||||||
|
+
|
||||||
|
+ g_clear_pointer (&match_info, g_match_info_free);
|
||||||
|
+ g_regex_unref (r);
|
||||||
|
+
|
||||||
|
+ if (inner_error)
|
||||||
|
+ g_propagate_error (error, inner_error);
|
||||||
|
+
|
||||||
|
+ return mask;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/*************************************************************************/
|
||||||
|
+
|
||||||
|
/* +CREG: <stat> (GSM 07.07 CREG=1 unsolicited) */
|
||||||
|
#define CREG1 "\\+(CREG|CGREG|CEREG):\\s*0*([0-9])"
|
||||||
|
|
||||||
|
diff --git a/src/mm-modem-helpers.h b/src/mm-modem-helpers.h
|
||||||
|
index a74924e353ad..bd10b940d1a4 100644
|
||||||
|
--- a/src/mm-modem-helpers.h
|
||||||
|
+++ b/src/mm-modem-helpers.h
|
||||||
|
@@ -95,6 +95,23 @@ GRegex *mm_voice_cring_regex_get(void);
|
||||||
|
GRegex *mm_voice_clip_regex_get (void);
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
+/* SERIAL specific helpers and utilities */
|
||||||
|
+
|
||||||
|
+/* AT+IFC=? response parser.
|
||||||
|
+ * For simplicity, we'll only consider flow control methods available in both
|
||||||
|
+ * TE and TA. */
|
||||||
|
+
|
||||||
|
+typedef enum {
|
||||||
|
+ MM_FLOW_CONTROL_UNKNOWN = 0,
|
||||||
|
+ MM_FLOW_CONTROL_NONE = 1 << 0, /* IFC=0,0 */
|
||||||
|
+ MM_FLOW_CONTROL_XON_XOFF = 1 << 1, /* IFC=1,1 */
|
||||||
|
+ MM_FLOW_CONTROL_RTS_CTS = 1 << 2, /* IFC=2,2 */
|
||||||
|
+} MMFlowControl;
|
||||||
|
+
|
||||||
|
+MMFlowControl mm_parse_ifc_test_response (const gchar *response,
|
||||||
|
+ GError **error);
|
||||||
|
+
|
||||||
|
+/*****************************************************************************/
|
||||||
|
/* 3GPP specific helpers and utilities */
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
diff --git a/src/tests/test-modem-helpers.c b/src/tests/test-modem-helpers.c
|
||||||
|
index ae0eec99f969..b59c5900a832 100644
|
||||||
|
--- a/src/tests/test-modem-helpers.c
|
||||||
|
+++ b/src/tests/test-modem-helpers.c
|
||||||
|
@@ -33,6 +33,93 @@
|
||||||
|
g_assert_cmpfloat (fabs (val1 - val2), <, tolerance)
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
+/* Test IFC=? responses */
|
||||||
|
+
|
||||||
|
+static void
|
||||||
|
+test_ifc_response (const gchar *str,
|
||||||
|
+ const MMFlowControl expected)
|
||||||
|
+{
|
||||||
|
+ MMFlowControl mask;
|
||||||
|
+ GError *error = NULL;
|
||||||
|
+
|
||||||
|
+ mask = mm_parse_ifc_test_response (str, &error);
|
||||||
|
+ g_assert_no_error (error);
|
||||||
|
+ g_assert_cmpuint (mask, ==, expected);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void
|
||||||
|
+test_ifc_response_all_simple (void)
|
||||||
|
+{
|
||||||
|
+ test_ifc_response ("+IFC (0,1,2),(0,1,2)", (MM_FLOW_CONTROL_NONE | MM_FLOW_CONTROL_XON_XOFF | MM_FLOW_CONTROL_RTS_CTS));
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void
|
||||||
|
+test_ifc_response_all_groups (void)
|
||||||
|
+{
|
||||||
|
+ test_ifc_response ("+IFC (0-2),(0-2)", (MM_FLOW_CONTROL_NONE | MM_FLOW_CONTROL_XON_XOFF | MM_FLOW_CONTROL_RTS_CTS));
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void
|
||||||
|
+test_ifc_response_none_only (void)
|
||||||
|
+{
|
||||||
|
+ test_ifc_response ("+IFC (0),(0)", MM_FLOW_CONTROL_NONE);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void
|
||||||
|
+test_ifc_response_xon_xoff_only (void)
|
||||||
|
+{
|
||||||
|
+ test_ifc_response ("+IFC (1),(1)", MM_FLOW_CONTROL_XON_XOFF);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void
|
||||||
|
+test_ifc_response_rts_cts_only (void)
|
||||||
|
+{
|
||||||
|
+ test_ifc_response ("+IFC (2),(2)", MM_FLOW_CONTROL_RTS_CTS);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void
|
||||||
|
+test_ifc_response_no_xon_xoff (void)
|
||||||
|
+{
|
||||||
|
+ test_ifc_response ("+IFC (0,2),(0,2)", (MM_FLOW_CONTROL_NONE | MM_FLOW_CONTROL_RTS_CTS));
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void
|
||||||
|
+test_ifc_response_no_xon_xoff_in_ta (void)
|
||||||
|
+{
|
||||||
|
+ test_ifc_response ("+IFC (0,1,2),(0,2)", (MM_FLOW_CONTROL_NONE | MM_FLOW_CONTROL_RTS_CTS));
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void
|
||||||
|
+test_ifc_response_no_xon_xoff_in_te (void)
|
||||||
|
+{
|
||||||
|
+ test_ifc_response ("+IFC (0,2),(0,1,2)", (MM_FLOW_CONTROL_NONE | MM_FLOW_CONTROL_RTS_CTS));
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void
|
||||||
|
+test_ifc_response_no_rts_cts_simple (void)
|
||||||
|
+{
|
||||||
|
+ test_ifc_response ("+IFC (0,1),(0,1)", (MM_FLOW_CONTROL_NONE | MM_FLOW_CONTROL_XON_XOFF));
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void
|
||||||
|
+test_ifc_response_no_rts_cts_groups (void)
|
||||||
|
+{
|
||||||
|
+ test_ifc_response ("+IFC (0-1),(0-1)", (MM_FLOW_CONTROL_NONE | MM_FLOW_CONTROL_XON_XOFF));
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void
|
||||||
|
+test_ifc_response_all_simple_and_unknown (void)
|
||||||
|
+{
|
||||||
|
+ test_ifc_response ("+IFC (0,1,2,3),(0,1,2)", (MM_FLOW_CONTROL_NONE | MM_FLOW_CONTROL_XON_XOFF | MM_FLOW_CONTROL_RTS_CTS));
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void
|
||||||
|
+test_ifc_response_all_groups_and_unknown (void)
|
||||||
|
+{
|
||||||
|
+ test_ifc_response ("+IFC (0-3),(0-2)", (MM_FLOW_CONTROL_NONE | MM_FLOW_CONTROL_XON_XOFF | MM_FLOW_CONTROL_RTS_CTS));
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/*****************************************************************************/
|
||||||
|
/* Test WS46=? responses */
|
||||||
|
|
||||||
|
static void
|
||||||
|
@@ -3536,6 +3623,19 @@ int main (int argc, char **argv)
|
||||||
|
suite = g_test_get_root ();
|
||||||
|
reg_data = reg_test_data_new ();
|
||||||
|
|
||||||
|
+ g_test_suite_add (suite, TESTCASE (test_ifc_response_all_simple, NULL));
|
||||||
|
+ g_test_suite_add (suite, TESTCASE (test_ifc_response_all_groups, NULL));
|
||||||
|
+ g_test_suite_add (suite, TESTCASE (test_ifc_response_none_only, NULL));
|
||||||
|
+ g_test_suite_add (suite, TESTCASE (test_ifc_response_xon_xoff_only, NULL));
|
||||||
|
+ g_test_suite_add (suite, TESTCASE (test_ifc_response_rts_cts_only, NULL));
|
||||||
|
+ g_test_suite_add (suite, TESTCASE (test_ifc_response_no_xon_xoff, NULL));
|
||||||
|
+ g_test_suite_add (suite, TESTCASE (test_ifc_response_no_xon_xoff_in_ta, NULL));
|
||||||
|
+ g_test_suite_add (suite, TESTCASE (test_ifc_response_no_xon_xoff_in_te, NULL));
|
||||||
|
+ g_test_suite_add (suite, TESTCASE (test_ifc_response_no_rts_cts_simple, NULL));
|
||||||
|
+ g_test_suite_add (suite, TESTCASE (test_ifc_response_no_rts_cts_groups, NULL));
|
||||||
|
+ g_test_suite_add (suite, TESTCASE (test_ifc_response_all_simple_and_unknown, NULL));
|
||||||
|
+ g_test_suite_add (suite, TESTCASE (test_ifc_response_all_groups_and_unknown, NULL));
|
||||||
|
+
|
||||||
|
g_test_suite_add (suite, TESTCASE (test_ws46_response_generic_2g3g4g, NULL));
|
||||||
|
g_test_suite_add (suite, TESTCASE (test_ws46_response_generic_2g3g, NULL));
|
||||||
|
g_test_suite_add (suite, TESTCASE (test_ws46_response_generic_2g3g_v2, NULL));
|
||||||
|
|
@ -0,0 +1,113 @@
|
||||||
|
From: Aleksander Morgado <aleksander@aleksander.es>
|
||||||
|
Date: Sat, 25 Mar 2017 01:11:45 +0100
|
||||||
|
Subject: [PATCH] broadband-modem: query supported flow control modes before
|
||||||
|
setting
|
||||||
|
|
||||||
|
Instead of assuming XON/XOFF is supported, we query the supported flow
|
||||||
|
control modes, and then we set the best one based on that, preferring
|
||||||
|
hardware flow control over software flow control.
|
||||||
|
---
|
||||||
|
src/mm-broadband-modem.c | 79 ++++++++++++++++++++++++++++++++++++------------
|
||||||
|
1 file changed, 60 insertions(+), 19 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/src/mm-broadband-modem.c b/src/mm-broadband-modem.c
|
||||||
|
index 97a268a2eb81..e5ad354882bb 100644
|
||||||
|
--- a/src/mm-broadband-modem.c
|
||||||
|
+++ b/src/mm-broadband-modem.c
|
||||||
|
@@ -3093,12 +3093,59 @@ modem_load_supported_charsets (MMIfaceModem *self,
|
||||||
|
/* configuring flow control (Modem interface) */
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
-modem_setup_flow_control_finish (MMIfaceModem *self,
|
||||||
|
- GAsyncResult *res,
|
||||||
|
- GError **error)
|
||||||
|
+modem_setup_flow_control_finish (MMIfaceModem *self,
|
||||||
|
+ GAsyncResult *res,
|
||||||
|
+ GError **error)
|
||||||
|
{
|
||||||
|
- /* Completely ignore errors */
|
||||||
|
- return TRUE;
|
||||||
|
+ return g_task_propagate_boolean (G_TASK (res), error);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static void
|
||||||
|
+ifc_test_ready (MMBaseModem *self,
|
||||||
|
+ GAsyncResult *res,
|
||||||
|
+ GTask *task)
|
||||||
|
+{
|
||||||
|
+ GError *error = NULL;
|
||||||
|
+ const gchar *response;
|
||||||
|
+ MMFlowControl mask;
|
||||||
|
+ const gchar *cmd;
|
||||||
|
+
|
||||||
|
+ /* Completely ignore errors in AT+IFC=? */
|
||||||
|
+ response = mm_base_modem_at_command_finish (self, res, &error);
|
||||||
|
+ if (!response)
|
||||||
|
+ goto out;
|
||||||
|
+
|
||||||
|
+ /* Parse response */
|
||||||
|
+ mask = mm_parse_ifc_test_response (response, &error);
|
||||||
|
+ if (mask == MM_FLOW_CONTROL_UNKNOWN)
|
||||||
|
+ goto out;
|
||||||
|
+
|
||||||
|
+ /* We prefer the methods in this order:
|
||||||
|
+ * RTS/CTS
|
||||||
|
+ * XON/XOFF
|
||||||
|
+ * None.
|
||||||
|
+ */
|
||||||
|
+ if (mask & MM_FLOW_CONTROL_RTS_CTS)
|
||||||
|
+ cmd = "+IFC=2,2";
|
||||||
|
+ else if (mask & MM_FLOW_CONTROL_XON_XOFF)
|
||||||
|
+ cmd = "+IFC=1,1";
|
||||||
|
+ else if (mask & MM_FLOW_CONTROL_NONE)
|
||||||
|
+ cmd = "+IFC=0,0";
|
||||||
|
+ else
|
||||||
|
+ g_assert_not_reached ();
|
||||||
|
+
|
||||||
|
+ /* Set flow control settings and ignore result */
|
||||||
|
+ mm_base_modem_at_command (self, cmd, 3, FALSE, NULL, NULL);
|
||||||
|
+
|
||||||
|
+out:
|
||||||
|
+ /* Ignore errors */
|
||||||
|
+ if (error) {
|
||||||
|
+ mm_dbg ("couldn't load supported flow control methods: %s", error->message);
|
||||||
|
+ g_error_free (error);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ g_task_return_boolean (task, TRUE);
|
||||||
|
+ g_object_unref (task);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
@@ -3106,23 +3153,17 @@ modem_setup_flow_control (MMIfaceModem *self,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
- GSimpleAsyncResult *result;
|
||||||
|
+ GTask *task;
|
||||||
|
+
|
||||||
|
+ task = g_task_new (self, NULL, callback, user_data);
|
||||||
|
|
||||||
|
- /* By default, try to set XOFF/XON flow control */
|
||||||
|
+ /* Query supported flow control methods */
|
||||||
|
mm_base_modem_at_command (MM_BASE_MODEM (self),
|
||||||
|
- "+IFC=1,1",
|
||||||
|
+ "+IFC=?",
|
||||||
|
3,
|
||||||
|
- FALSE,
|
||||||
|
- NULL,
|
||||||
|
- NULL);
|
||||||
|
-
|
||||||
|
- result = g_simple_async_result_new (G_OBJECT (self),
|
||||||
|
- callback,
|
||||||
|
- user_data,
|
||||||
|
- modem_setup_flow_control);
|
||||||
|
- g_simple_async_result_set_op_res_gboolean (result, TRUE);
|
||||||
|
- g_simple_async_result_complete_in_idle (result);
|
||||||
|
- g_object_unref (result);
|
||||||
|
+ TRUE,
|
||||||
|
+ (GAsyncReadyCallback)ifc_test_ready,
|
||||||
|
+ task);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
From: Aleksander Morgado <aleksander@aleksander.es>
|
||||||
|
Date: Sat, 25 Mar 2017 01:14:45 +0100
|
||||||
|
Subject: [PATCH] wavecom: ignore custom flow control handling
|
||||||
|
|
||||||
|
The generic modem object already supports querying for the supported
|
||||||
|
methods and setting the best one found.
|
||||||
|
---
|
||||||
|
plugins/wavecom/mm-broadband-modem-wavecom.c | 27 ---------------------------
|
||||||
|
1 file changed, 27 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/plugins/wavecom/mm-broadband-modem-wavecom.c b/plugins/wavecom/mm-broadband-modem-wavecom.c
|
||||||
|
index 47c6b4f040a0..0e93e47ec701 100644
|
||||||
|
--- a/plugins/wavecom/mm-broadband-modem-wavecom.c
|
||||||
|
+++ b/plugins/wavecom/mm-broadband-modem-wavecom.c
|
||||||
|
@@ -1242,31 +1242,6 @@ modem_after_sim_unlock (MMIfaceModem *self,
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
-/* Flow control (Modem interface) */
|
||||||
|
-
|
||||||
|
-static gboolean
|
||||||
|
-setup_flow_control_finish (MMIfaceModem *self,
|
||||||
|
- GAsyncResult *res,
|
||||||
|
- GError **error)
|
||||||
|
-{
|
||||||
|
- return !!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error);
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static void
|
||||||
|
-setup_flow_control (MMIfaceModem *self,
|
||||||
|
- GAsyncReadyCallback callback,
|
||||||
|
- gpointer user_data)
|
||||||
|
-{
|
||||||
|
- /* Wavecom doesn't have XOFF/XON flow control, so we enable RTS/CTS */
|
||||||
|
- mm_base_modem_at_command (MM_BASE_MODEM (self),
|
||||||
|
- "+IFC=2,2",
|
||||||
|
- 3,
|
||||||
|
- FALSE,
|
||||||
|
- callback,
|
||||||
|
- user_data);
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-/*****************************************************************************/
|
||||||
|
/* Modem power up (Modem interface) */
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
@@ -1420,8 +1395,6 @@ iface_modem_init (MMIfaceModem *iface)
|
||||||
|
iface->load_access_technologies_finish = load_access_technologies_finish;
|
||||||
|
iface->modem_after_sim_unlock = modem_after_sim_unlock;
|
||||||
|
iface->modem_after_sim_unlock_finish = modem_after_sim_unlock_finish;
|
||||||
|
- iface->setup_flow_control = setup_flow_control;
|
||||||
|
- iface->setup_flow_control_finish = setup_flow_control_finish;
|
||||||
|
iface->modem_power_up = modem_power_up;
|
||||||
|
iface->modem_power_up_finish = modem_power_up_finish;
|
||||||
|
iface->modem_power_down = modem_power_down;
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
From: Aleksander Morgado <aleksander@aleksander.es>
|
||||||
|
Date: Sat, 25 Mar 2017 01:16:25 +0100
|
||||||
|
Subject: [PATCH] telit: ignore custom flow control handling
|
||||||
|
|
||||||
|
The generic modem object already supports querying for the supported
|
||||||
|
methods and setting the best one found.
|
||||||
|
---
|
||||||
|
plugins/telit/mm-broadband-modem-telit.c | 48 --------------------------------
|
||||||
|
1 file changed, 48 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/plugins/telit/mm-broadband-modem-telit.c b/plugins/telit/mm-broadband-modem-telit.c
|
||||||
|
index 8b8731062966..cce02298352b 100644
|
||||||
|
--- a/plugins/telit/mm-broadband-modem-telit.c
|
||||||
|
+++ b/plugins/telit/mm-broadband-modem-telit.c
|
||||||
|
@@ -885,52 +885,6 @@ load_access_technologies (MMIfaceModem *self,
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
-/* Flow control (Modem interface) */
|
||||||
|
-
|
||||||
|
-static gboolean
|
||||||
|
-setup_flow_control_finish (MMIfaceModem *self,
|
||||||
|
- GAsyncResult *res,
|
||||||
|
- GError **error)
|
||||||
|
-{
|
||||||
|
- /* Completely ignore errors */
|
||||||
|
- return TRUE;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-static void
|
||||||
|
-setup_flow_control (MMIfaceModem *self,
|
||||||
|
- GAsyncReadyCallback callback,
|
||||||
|
- gpointer user_data)
|
||||||
|
-{
|
||||||
|
- GSimpleAsyncResult *result;
|
||||||
|
- gchar *cmd;
|
||||||
|
- guint flow_control = 1; /* Default flow control: XON/XOFF */
|
||||||
|
-
|
||||||
|
- switch (mm_base_modem_get_product_id (MM_BASE_MODEM (self)) & 0xFFFF) {
|
||||||
|
- case 0x0021:
|
||||||
|
- flow_control = 2; /* Telit IMC modems support only RTS/CTS mode */
|
||||||
|
- break;
|
||||||
|
- default:
|
||||||
|
- break;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- cmd = g_strdup_printf ("+IFC=%u,%u", flow_control, flow_control);
|
||||||
|
- mm_base_modem_at_command (MM_BASE_MODEM (self),
|
||||||
|
- cmd,
|
||||||
|
- 3,
|
||||||
|
- FALSE,
|
||||||
|
- NULL,
|
||||||
|
- NULL);
|
||||||
|
- result = g_simple_async_result_new (G_OBJECT (self),
|
||||||
|
- callback,
|
||||||
|
- user_data,
|
||||||
|
- setup_flow_control);
|
||||||
|
- g_simple_async_result_set_op_res_gboolean (result, TRUE);
|
||||||
|
- g_simple_async_result_complete_in_idle (result);
|
||||||
|
- g_object_unref (result);
|
||||||
|
- g_free (cmd);
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
-/*****************************************************************************/
|
||||||
|
/* Load current mode (Modem interface) */
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
@@ -1287,8 +1241,6 @@ iface_modem_init (MMIfaceModem *iface)
|
||||||
|
iface->modem_power_down_finish = modem_power_down_finish;
|
||||||
|
iface->load_access_technologies = load_access_technologies;
|
||||||
|
iface->load_access_technologies_finish = load_access_technologies_finish;
|
||||||
|
- iface->setup_flow_control = setup_flow_control;
|
||||||
|
- iface->setup_flow_control_finish = setup_flow_control_finish;
|
||||||
|
iface->load_supported_modes = load_supported_modes;
|
||||||
|
iface->load_supported_modes_finish = load_supported_modes_finish;
|
||||||
|
iface->load_current_modes = load_current_modes;
|
||||||
|
|
@ -0,0 +1,167 @@
|
||||||
|
From: Aleksander Morgado <aleksander@aleksander.es>
|
||||||
|
Date: Sat, 25 Mar 2017 18:17:41 +0100
|
||||||
|
Subject: [PATCH] port-serial: new internal method to run tcsetattr()
|
||||||
|
|
||||||
|
The method takes care of looping if EAGAIN errors happen, as well as
|
||||||
|
checking whether all attributes were set or not.
|
||||||
|
---
|
||||||
|
src/mm-port-serial.c | 119 ++++++++++++++++++++++++---------------------------
|
||||||
|
1 file changed, 57 insertions(+), 62 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/src/mm-port-serial.c b/src/mm-port-serial.c
|
||||||
|
index 2b6b92d3d1da..f2b97a02486d 100644
|
||||||
|
--- a/src/mm-port-serial.c
|
||||||
|
+++ b/src/mm-port-serial.c
|
||||||
|
@@ -347,9 +347,62 @@ parse_stopbits (guint i)
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
+internal_tcsetattr (MMPortSerial *self,
|
||||||
|
+ gint fd,
|
||||||
|
+ const struct termios *options,
|
||||||
|
+ GError **error)
|
||||||
|
+{
|
||||||
|
+ guint count;
|
||||||
|
+ struct termios other;
|
||||||
|
+
|
||||||
|
+#define MAX_TCSETATTR_RETRIES 4
|
||||||
|
+
|
||||||
|
+ for (count = 0; count < MAX_TCSETATTR_RETRIES; count++) {
|
||||||
|
+ /* try to set the new port attributes */
|
||||||
|
+ errno = 0;
|
||||||
|
+ if (tcsetattr (fd, TCSANOW, options) == 0)
|
||||||
|
+ break;
|
||||||
|
+
|
||||||
|
+ /* hard error if not EAGAIN */
|
||||||
|
+ if (errno != EAGAIN) {
|
||||||
|
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
||||||
|
+ "couldn't set serial port attributes: %s", g_strerror (errno));
|
||||||
|
+ return FALSE;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /* try a few times if EAGAIN */
|
||||||
|
+ g_usleep (100000);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /* too many retries? */
|
||||||
|
+ if (count == MAX_TCSETATTR_RETRIES) {
|
||||||
|
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
||||||
|
+ "couldn't set serial port attributes: too many retries (%u)", count);
|
||||||
|
+ return FALSE;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /* tcsetattr() returns 0 if any of the requested attributes could be set,
|
||||||
|
+ * so we should double-check that all were set and log if not. Just with
|
||||||
|
+ * debug level, as we're ignoring this issue all together anyway.
|
||||||
|
+ */
|
||||||
|
+ memset (&other, 0, sizeof (struct termios));
|
||||||
|
+ errno = 0;
|
||||||
|
+ if (tcgetattr (fd, &other) != 0)
|
||||||
|
+ mm_dbg ("(%s): couldn't get serial port attributes after setting them: %s",
|
||||||
|
+ mm_port_get_device (MM_PORT (self)), g_strerror (errno));
|
||||||
|
+ else if (memcmp (options, &other, sizeof (struct termios)) != 0)
|
||||||
|
+ mm_dbg ("(%s): port attributes not fully set",
|
||||||
|
+ mm_port_get_device (MM_PORT (self)));
|
||||||
|
+
|
||||||
|
+#undef MAX_TCSETATTR_RETRIES
|
||||||
|
+
|
||||||
|
+ return TRUE;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static gboolean
|
||||||
|
real_config_fd (MMPortSerial *self, int fd, GError **error)
|
||||||
|
{
|
||||||
|
- struct termios stbuf, other;
|
||||||
|
+ struct termios stbuf;
|
||||||
|
guint speed;
|
||||||
|
gint bits;
|
||||||
|
gint parity;
|
||||||
|
@@ -416,32 +469,7 @@ real_config_fd (MMPortSerial *self, int fd, GError **error)
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
- if (tcsetattr (fd, TCSANOW, &stbuf) < 0) {
|
||||||
|
- g_set_error (error,
|
||||||
|
- MM_CORE_ERROR,
|
||||||
|
- MM_CORE_ERROR_FAILED,
|
||||||
|
- "%s: failed to set serial port attributes; errno %d",
|
||||||
|
- __func__, errno);
|
||||||
|
- return FALSE;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* tcsetattr() returns 0 if any of the requested attributes could be set,
|
||||||
|
- * so we should double-check that all were set and log a warning if not.
|
||||||
|
- */
|
||||||
|
- memset (&other, 0, sizeof (struct termios));
|
||||||
|
- errno = 0;
|
||||||
|
- if (tcgetattr (fd, &other) != 0) {
|
||||||
|
- mm_warn ("(%s): tcgetattr() error: %d",
|
||||||
|
- mm_port_get_device (MM_PORT (self)),
|
||||||
|
- errno);
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- if (memcmp (&stbuf, &other, sizeof (other)) != 0) {
|
||||||
|
- mm_warn ("(%s): port attributes not fully set",
|
||||||
|
- mm_port_get_device (MM_PORT (self)));
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- return TRUE;
|
||||||
|
+ return internal_tcsetattr (self, fd, &stbuf, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
@@ -1576,15 +1604,11 @@ static gboolean
|
||||||
|
set_speed (MMPortSerial *self, speed_t speed, GError **error)
|
||||||
|
{
|
||||||
|
struct termios options;
|
||||||
|
- int fd, count = 4;
|
||||||
|
- gboolean success = FALSE;
|
||||||
|
|
||||||
|
g_assert (self->priv->fd >= 0);
|
||||||
|
|
||||||
|
- fd = self->priv->fd;
|
||||||
|
-
|
||||||
|
memset (&options, 0, sizeof (struct termios));
|
||||||
|
- if (tcgetattr (fd, &options) != 0) {
|
||||||
|
+ if (tcgetattr (self->priv->fd, &options) != 0) {
|
||||||
|
g_set_error (error,
|
||||||
|
MM_CORE_ERROR,
|
||||||
|
MM_CORE_ERROR_FAILED,
|
||||||
|
@@ -1601,36 +1625,7 @@ set_speed (MMPortSerial *self, speed_t speed, GError **error)
|
||||||
|
if (self->priv->rts_cts)
|
||||||
|
options.c_cflag |= (CRTSCTS);
|
||||||
|
|
||||||
|
- while (count-- > 0) {
|
||||||
|
- if (tcsetattr (fd, TCSANOW, &options) == 0) {
|
||||||
|
- success = TRUE;
|
||||||
|
- break; /* Operation successful */
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- /* Try a few times if EAGAIN */
|
||||||
|
- if (errno == EAGAIN)
|
||||||
|
- g_usleep (100000);
|
||||||
|
- else {
|
||||||
|
- /* If not EAGAIN, hard error */
|
||||||
|
- g_set_error (error,
|
||||||
|
- MM_CORE_ERROR,
|
||||||
|
- MM_CORE_ERROR_FAILED,
|
||||||
|
- "%s: tcsetattr() error %d",
|
||||||
|
- __func__, errno);
|
||||||
|
- return FALSE;
|
||||||
|
- }
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- if (!success) {
|
||||||
|
- g_set_error (error,
|
||||||
|
- MM_CORE_ERROR,
|
||||||
|
- MM_CORE_ERROR_FAILED,
|
||||||
|
- "%s: tcsetattr() retry timeout",
|
||||||
|
- __func__);
|
||||||
|
- return FALSE;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- return TRUE;
|
||||||
|
+ return internal_tcsetattr (self, self->priv->fd, &options, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
@ -0,0 +1,92 @@
|
||||||
|
From: Aleksander Morgado <aleksander@aleksander.es>
|
||||||
|
Date: Sat, 25 Mar 2017 18:18:46 +0100
|
||||||
|
Subject: [PATCH] port-serial: new method to explicitly set flow control
|
||||||
|
settings
|
||||||
|
|
||||||
|
---
|
||||||
|
src/mm-port-serial.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||||
|
src/mm-port-serial.h | 5 +++++
|
||||||
|
2 files changed, 55 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/src/mm-port-serial.c b/src/mm-port-serial.c
|
||||||
|
index f2b97a02486d..e55f90625020 100644
|
||||||
|
--- a/src/mm-port-serial.c
|
||||||
|
+++ b/src/mm-port-serial.c
|
||||||
|
@@ -1778,6 +1778,56 @@ mm_port_serial_flash (MMPortSerial *self,
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
+gboolean
|
||||||
|
+mm_port_serial_set_flow_control (MMPortSerial *self,
|
||||||
|
+ MMFlowControl flow_control,
|
||||||
|
+ GError **error)
|
||||||
|
+{
|
||||||
|
+ struct termios options;
|
||||||
|
+ gboolean had_xon_xoff;
|
||||||
|
+ gboolean had_rts_cts;
|
||||||
|
+
|
||||||
|
+ /* retrieve current settings */
|
||||||
|
+ memset (&options, 0, sizeof (struct termios));
|
||||||
|
+ if (tcgetattr (self->priv->fd, &options) != 0) {
|
||||||
|
+ g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
||||||
|
+ "couldn't get serial port attributes: %s", g_strerror (errno));
|
||||||
|
+ return FALSE;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /* clear all flow control flags */
|
||||||
|
+
|
||||||
|
+ had_xon_xoff = !!(options.c_iflag & (IXON | IXOFF));
|
||||||
|
+ options.c_iflag &= ~(IXON | IXOFF | IXANY);
|
||||||
|
+
|
||||||
|
+ had_rts_cts = !!(options.c_cflag & (CRTSCTS));
|
||||||
|
+ options.c_cflag &= ~(CRTSCTS);
|
||||||
|
+
|
||||||
|
+ /* setup the requested flags */
|
||||||
|
+ switch (flow_control) {
|
||||||
|
+ case MM_FLOW_CONTROL_XON_XOFF:
|
||||||
|
+ mm_dbg ("(%s): enabling XON/XOFF flow control", mm_port_get_device (MM_PORT (self)));
|
||||||
|
+ options.c_iflag |= (IXON | IXOFF | IXANY);
|
||||||
|
+ break;
|
||||||
|
+ case MM_FLOW_CONTROL_RTS_CTS:
|
||||||
|
+ mm_dbg ("(%s): enabling RTS/CTS flow control", mm_port_get_device (MM_PORT (self)));
|
||||||
|
+ options.c_cflag |= (CRTSCTS);
|
||||||
|
+ break;
|
||||||
|
+ case MM_FLOW_CONTROL_NONE:
|
||||||
|
+ if (had_xon_xoff)
|
||||||
|
+ mm_dbg ("(%s): disabling XON/XOFF flow control", mm_port_get_device (MM_PORT (self)));
|
||||||
|
+ if (had_rts_cts)
|
||||||
|
+ mm_dbg ("(%s): disabling RTS/CTS flow control", mm_port_get_device (MM_PORT (self)));
|
||||||
|
+ break;
|
||||||
|
+ default:
|
||||||
|
+ g_assert_not_reached ();
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return internal_tcsetattr (self, self->priv->fd, &options, error);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+/*****************************************************************************/
|
||||||
|
+
|
||||||
|
MMPortSerial *
|
||||||
|
mm_port_serial_new (const char *name, MMPortType ptype)
|
||||||
|
{
|
||||||
|
diff --git a/src/mm-port-serial.h b/src/mm-port-serial.h
|
||||||
|
index 708e39123412..223b9ce1c4a4 100644
|
||||||
|
--- a/src/mm-port-serial.h
|
||||||
|
+++ b/src/mm-port-serial.h
|
||||||
|
@@ -21,6 +21,7 @@
|
||||||
|
#include <glib-object.h>
|
||||||
|
#include <gio/gio.h>
|
||||||
|
|
||||||
|
+#include "mm-modem-helpers.h"
|
||||||
|
#include "mm-port.h"
|
||||||
|
|
||||||
|
#define MM_TYPE_PORT_SERIAL (mm_port_serial_get_type ())
|
||||||
|
@@ -150,4 +151,8 @@ GByteArray *mm_port_serial_command_finish (MMPortSerial *self,
|
||||||
|
GAsyncResult *res,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
+gboolean mm_port_serial_set_flow_control (MMPortSerial *self,
|
||||||
|
+ MMFlowControl flow_control,
|
||||||
|
+ GError **error);
|
||||||
|
+
|
||||||
|
#endif /* MM_PORT_SERIAL_H */
|
||||||
|
|
@ -0,0 +1,118 @@
|
||||||
|
From: Aleksander Morgado <aleksander@aleksander.es>
|
||||||
|
Date: Sat, 25 Mar 2017 18:27:09 +0100
|
||||||
|
Subject: [PATCH] port-serial: remove all default flow control settings
|
||||||
|
|
||||||
|
We won't set XON/XOFF by default and we won't allow setting RTS/CTS
|
||||||
|
via a property. The serial port by default starts with no flow control
|
||||||
|
configured.
|
||||||
|
---
|
||||||
|
src/mm-port-serial.c | 34 ++++++----------------------------
|
||||||
|
src/mm-port-serial.h | 1 -
|
||||||
|
2 files changed, 6 insertions(+), 29 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/src/mm-port-serial.c b/src/mm-port-serial.c
|
||||||
|
index e55f90625020..04ab17fe1675 100644
|
||||||
|
--- a/src/mm-port-serial.c
|
||||||
|
+++ b/src/mm-port-serial.c
|
||||||
|
@@ -56,7 +56,6 @@ enum {
|
||||||
|
PROP_SEND_DELAY,
|
||||||
|
PROP_FD,
|
||||||
|
PROP_SPEW_CONTROL,
|
||||||
|
- PROP_RTS_CTS,
|
||||||
|
PROP_FLASH_OK,
|
||||||
|
|
||||||
|
LAST_PROP
|
||||||
|
@@ -97,7 +96,6 @@ struct _MMPortSerialPrivate {
|
||||||
|
guint stopbits;
|
||||||
|
guint64 send_delay;
|
||||||
|
gboolean spew_control;
|
||||||
|
- gboolean rts_cts;
|
||||||
|
gboolean flash_ok;
|
||||||
|
|
||||||
|
guint queue_id;
|
||||||
|
@@ -433,21 +431,19 @@ real_config_fd (MMPortSerial *self, int fd, GError **error)
|
||||||
|
errno);
|
||||||
|
}
|
||||||
|
|
||||||
|
- stbuf.c_iflag &= ~(IGNCR | ICRNL | IUCLC | INPCK | IXON | IXANY );
|
||||||
|
+ stbuf.c_cflag &= ~(CBAUD | CSIZE | CSTOPB | PARENB | PARODD | CRTSCTS);
|
||||||
|
+ stbuf.c_iflag &= ~(IGNCR | ICRNL | IUCLC | INPCK | IXON | IXOFF | IXANY );
|
||||||
|
stbuf.c_oflag &= ~(OPOST | OLCUC | OCRNL | ONLCR | ONLRET);
|
||||||
|
stbuf.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHONL);
|
||||||
|
stbuf.c_cc[VMIN] = 1;
|
||||||
|
stbuf.c_cc[VTIME] = 0;
|
||||||
|
stbuf.c_cc[VEOF] = 1;
|
||||||
|
|
||||||
|
- /* Use software handshaking and ignore parity/framing errors */
|
||||||
|
- stbuf.c_iflag |= (IXON | IXOFF | IXANY | IGNPAR);
|
||||||
|
+ /* Ignore parity/framing errors */
|
||||||
|
+ stbuf.c_iflag |= IGNPAR;
|
||||||
|
|
||||||
|
- /* Set up port speed and serial attributes; also ignore modem control
|
||||||
|
- * lines since most drivers don't implement RTS/CTS anyway.
|
||||||
|
- */
|
||||||
|
- stbuf.c_cflag &= ~(CBAUD | CSIZE | CSTOPB | PARENB | CRTSCTS);
|
||||||
|
- stbuf.c_cflag |= (bits | CREAD | 0 | parity | stopbits | CLOCAL);
|
||||||
|
+ /* Set up port speed and serial attributes and enable receiver in local mode */
|
||||||
|
+ stbuf.c_cflag |= (bits | parity | stopbits | CLOCAL | CREAD);
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
if (cfsetispeed (&stbuf, speed) != 0) {
|
||||||
|
@@ -1621,10 +1617,6 @@ set_speed (MMPortSerial *self, speed_t speed, GError **error)
|
||||||
|
cfsetospeed (&options, speed);
|
||||||
|
options.c_cflag |= (CLOCAL | CREAD);
|
||||||
|
|
||||||
|
- /* Configure flow control as well here */
|
||||||
|
- if (self->priv->rts_cts)
|
||||||
|
- options.c_cflag |= (CRTSCTS);
|
||||||
|
-
|
||||||
|
return internal_tcsetattr (self, self->priv->fd, &options, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -1928,9 +1920,6 @@ set_property (GObject *object,
|
||||||
|
case PROP_SPEW_CONTROL:
|
||||||
|
self->priv->spew_control = g_value_get_boolean (value);
|
||||||
|
break;
|
||||||
|
- case PROP_RTS_CTS:
|
||||||
|
- self->priv->rts_cts = g_value_get_boolean (value);
|
||||||
|
- break;
|
||||||
|
case PROP_FLASH_OK:
|
||||||
|
self->priv->flash_ok = g_value_get_boolean (value);
|
||||||
|
break;
|
||||||
|
@@ -1970,9 +1959,6 @@ get_property (GObject *object,
|
||||||
|
case PROP_SPEW_CONTROL:
|
||||||
|
g_value_set_boolean (value, self->priv->spew_control);
|
||||||
|
break;
|
||||||
|
- case PROP_RTS_CTS:
|
||||||
|
- g_value_set_boolean (value, self->priv->rts_cts);
|
||||||
|
- break;
|
||||||
|
case PROP_FLASH_OK:
|
||||||
|
g_value_set_boolean (value, self->priv->flash_ok);
|
||||||
|
break;
|
||||||
|
@@ -2081,14 +2067,6 @@ mm_port_serial_class_init (MMPortSerialClass *klass)
|
||||||
|
G_PARAM_READWRITE));
|
||||||
|
|
||||||
|
g_object_class_install_property
|
||||||
|
- (object_class, PROP_RTS_CTS,
|
||||||
|
- g_param_spec_boolean (MM_PORT_SERIAL_RTS_CTS,
|
||||||
|
- "RTSCTS",
|
||||||
|
- "Enable RTS/CTS flow control",
|
||||||
|
- FALSE,
|
||||||
|
- G_PARAM_READWRITE));
|
||||||
|
-
|
||||||
|
- g_object_class_install_property
|
||||||
|
(object_class, PROP_FLASH_OK,
|
||||||
|
g_param_spec_boolean (MM_PORT_SERIAL_FLASH_OK,
|
||||||
|
"FlashOk",
|
||||||
|
diff --git a/src/mm-port-serial.h b/src/mm-port-serial.h
|
||||||
|
index 223b9ce1c4a4..0514e3607101 100644
|
||||||
|
--- a/src/mm-port-serial.h
|
||||||
|
+++ b/src/mm-port-serial.h
|
||||||
|
@@ -36,7 +36,6 @@
|
||||||
|
#define MM_PORT_SERIAL_PARITY "parity"
|
||||||
|
#define MM_PORT_SERIAL_STOPBITS "stopbits"
|
||||||
|
#define MM_PORT_SERIAL_SEND_DELAY "send-delay"
|
||||||
|
-#define MM_PORT_SERIAL_RTS_CTS "rts-cts"
|
||||||
|
#define MM_PORT_SERIAL_FD "fd" /* Construct-only */
|
||||||
|
#define MM_PORT_SERIAL_SPEW_CONTROL "spew-control" /* Construct-only */
|
||||||
|
#define MM_PORT_SERIAL_FLASH_OK "flash-ok" /* Construct-only */
|
||||||
|
|
@ -0,0 +1,211 @@
|
||||||
|
From: Aleksander Morgado <aleksander@aleksander.es>
|
||||||
|
Date: Sat, 25 Mar 2017 18:30:02 +0100
|
||||||
|
Subject: [PATCH] broadband-bearer: once connected, set flow control settings
|
||||||
|
|
||||||
|
During modem initialization we detected the flow control settings
|
||||||
|
supported by the modem, and selected the best one to use from them,
|
||||||
|
notifying it to the device via AT+IFC. The device was therefore
|
||||||
|
instructed to use that flow control setting for data transmission in
|
||||||
|
the TTY (i.e. not during AT control commands).
|
||||||
|
|
||||||
|
The missing thing was to also configure ourselves our end of the
|
||||||
|
serial port with the same flow control settings when getting into data
|
||||||
|
mode. By doing it ourselves, we avoid requiring any explicit setting
|
||||||
|
in pppd for flow control; pppd can assume the flow control settings
|
||||||
|
are already the expected ones.
|
||||||
|
|
||||||
|
Worth noting that all this setup is completely ignored for TTYs
|
||||||
|
exposed directly via USB.
|
||||||
|
|
||||||
|
https://bugs.freedesktop.org/show_bug.cgi?id=100394
|
||||||
|
---
|
||||||
|
src/mm-broadband-bearer.c | 32 ++++++++++++++++++++++++++++++++
|
||||||
|
src/mm-broadband-modem.c | 36 +++++++++++++++++++++++++-----------
|
||||||
|
src/mm-broadband-modem.h | 4 ++++
|
||||||
|
3 files changed, 61 insertions(+), 11 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/src/mm-broadband-bearer.c b/src/mm-broadband-bearer.c
|
||||||
|
index 0565eee3afeb..facbc1457603 100644
|
||||||
|
--- a/src/mm-broadband-bearer.c
|
||||||
|
+++ b/src/mm-broadband-bearer.c
|
||||||
|
@@ -259,6 +259,14 @@ dial_cdma_ready (MMBaseModem *modem,
|
||||||
|
* connect_succeeded(), we do it right away so that we stop our polling. */
|
||||||
|
mm_port_set_connected (ctx->data, TRUE);
|
||||||
|
|
||||||
|
+ /* Configure flow control to use while connected */
|
||||||
|
+ if (!mm_port_serial_set_flow_control (MM_PORT_SERIAL (ctx->data),
|
||||||
|
+ mm_broadband_modem_get_connected_flow_control (MM_BROADBAND_MODEM (modem)),
|
||||||
|
+ &error)) {
|
||||||
|
+ mm_warn ("Couldn't set flow control settings: %s", error->message);
|
||||||
|
+ g_clear_error (&error);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
/* Keep port open during connection */
|
||||||
|
ctx->close_data_on_exit = FALSE;
|
||||||
|
|
||||||
|
@@ -557,6 +565,8 @@ atd_ready (MMBaseModem *modem,
|
||||||
|
GAsyncResult *res,
|
||||||
|
Dial3gppContext *ctx)
|
||||||
|
{
|
||||||
|
+ GError *error = NULL;
|
||||||
|
+
|
||||||
|
/* DO NOT check for cancellable here. If we got here without errors, the
|
||||||
|
* bearer is really connected and therefore we need to reflect that in
|
||||||
|
* the state machine. */
|
||||||
|
@@ -576,6 +586,14 @@ atd_ready (MMBaseModem *modem,
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ /* Configure flow control to use while connected */
|
||||||
|
+ if (!mm_port_serial_set_flow_control (MM_PORT_SERIAL (ctx->dial_port),
|
||||||
|
+ mm_broadband_modem_get_connected_flow_control (MM_BROADBAND_MODEM (modem)),
|
||||||
|
+ &error)) {
|
||||||
|
+ mm_warn ("Couldn't set flow control settings: %s", error->message);
|
||||||
|
+ g_clear_error (&error);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
/* The ATD command has succeeded, and therefore the TTY is in data mode now.
|
||||||
|
* Instead of waiting for setting the port as connected later in
|
||||||
|
* connect_succeeded(), we do it right away so that we stop our polling. */
|
||||||
|
@@ -1452,9 +1470,16 @@ data_flash_cdma_ready (MMPortSerial *data,
|
||||||
|
DetailedDisconnectContext *ctx)
|
||||||
|
{
|
||||||
|
GError *error = NULL;
|
||||||
|
+ GError *flow_control_error = NULL;
|
||||||
|
|
||||||
|
mm_port_serial_flash_finish (data, res, &error);
|
||||||
|
|
||||||
|
+ /* Cleanup flow control */
|
||||||
|
+ if (!mm_port_serial_set_flow_control (MM_PORT_SERIAL (data), MM_FLOW_CONTROL_NONE, &flow_control_error)) {
|
||||||
|
+ mm_dbg ("Couldn't reset flow control settings: %s", flow_control_error->message);
|
||||||
|
+ g_clear_error (&flow_control_error);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
/* We kept the serial port open during connection, now we close that open
|
||||||
|
* count */
|
||||||
|
mm_port_serial_close (data);
|
||||||
|
@@ -1569,9 +1594,16 @@ data_flash_3gpp_ready (MMPortSerial *data,
|
||||||
|
DetailedDisconnectContext *ctx)
|
||||||
|
{
|
||||||
|
GError *error = NULL;
|
||||||
|
+ GError *flow_control_error = NULL;
|
||||||
|
|
||||||
|
mm_port_serial_flash_finish (data, res, &error);
|
||||||
|
|
||||||
|
+ /* Cleanup flow control */
|
||||||
|
+ if (!mm_port_serial_set_flow_control (MM_PORT_SERIAL (data), MM_FLOW_CONTROL_NONE, &flow_control_error)) {
|
||||||
|
+ mm_dbg ("Couldn't reset flow control settings: %s", flow_control_error->message);
|
||||||
|
+ g_clear_error (&flow_control_error);
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
/* We kept the serial port open during connection, now we close that open
|
||||||
|
* count */
|
||||||
|
mm_port_serial_close (data);
|
||||||
|
diff --git a/src/mm-broadband-modem.c b/src/mm-broadband-modem.c
|
||||||
|
index e5ad354882bb..3aa6e5f37424 100644
|
||||||
|
--- a/src/mm-broadband-modem.c
|
||||||
|
+++ b/src/mm-broadband-modem.c
|
||||||
|
@@ -146,6 +146,7 @@ struct _MMBroadbandModemPrivate {
|
||||||
|
guint modem_cind_max_signal_quality;
|
||||||
|
guint modem_cind_indicator_roaming;
|
||||||
|
guint modem_cind_indicator_service;
|
||||||
|
+ MMFlowControl modem_flow_control;
|
||||||
|
|
||||||
|
/*<--- Modem 3GPP interface --->*/
|
||||||
|
/* Properties */
|
||||||
|
@@ -3101,17 +3102,20 @@ modem_setup_flow_control_finish (MMIfaceModem *self,
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
-ifc_test_ready (MMBaseModem *self,
|
||||||
|
+ifc_test_ready (MMBaseModem *_self,
|
||||||
|
GAsyncResult *res,
|
||||||
|
GTask *task)
|
||||||
|
{
|
||||||
|
- GError *error = NULL;
|
||||||
|
- const gchar *response;
|
||||||
|
- MMFlowControl mask;
|
||||||
|
- const gchar *cmd;
|
||||||
|
+ MMBroadbandModem *self;
|
||||||
|
+ GError *error = NULL;
|
||||||
|
+ const gchar *response;
|
||||||
|
+ MMFlowControl mask;
|
||||||
|
+ const gchar *cmd;
|
||||||
|
+
|
||||||
|
+ self = MM_BROADBAND_MODEM (_self);
|
||||||
|
|
||||||
|
/* Completely ignore errors in AT+IFC=? */
|
||||||
|
- response = mm_base_modem_at_command_finish (self, res, &error);
|
||||||
|
+ response = mm_base_modem_at_command_finish (_self, res, &error);
|
||||||
|
if (!response)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
@@ -3125,17 +3129,20 @@ ifc_test_ready (MMBaseModem *self,
|
||||||
|
* XON/XOFF
|
||||||
|
* None.
|
||||||
|
*/
|
||||||
|
- if (mask & MM_FLOW_CONTROL_RTS_CTS)
|
||||||
|
+ if (mask & MM_FLOW_CONTROL_RTS_CTS) {
|
||||||
|
+ self->priv->modem_flow_control = MM_FLOW_CONTROL_RTS_CTS;
|
||||||
|
cmd = "+IFC=2,2";
|
||||||
|
- else if (mask & MM_FLOW_CONTROL_XON_XOFF)
|
||||||
|
+ } else if (mask & MM_FLOW_CONTROL_XON_XOFF) {
|
||||||
|
+ self->priv->modem_flow_control = MM_FLOW_CONTROL_XON_XOFF;
|
||||||
|
cmd = "+IFC=1,1";
|
||||||
|
- else if (mask & MM_FLOW_CONTROL_NONE)
|
||||||
|
+ } else if (mask & MM_FLOW_CONTROL_NONE) {
|
||||||
|
+ self->priv->modem_flow_control = MM_FLOW_CONTROL_NONE;
|
||||||
|
cmd = "+IFC=0,0";
|
||||||
|
- else
|
||||||
|
+ } else
|
||||||
|
g_assert_not_reached ();
|
||||||
|
|
||||||
|
/* Set flow control settings and ignore result */
|
||||||
|
- mm_base_modem_at_command (self, cmd, 3, FALSE, NULL, NULL);
|
||||||
|
+ mm_base_modem_at_command (_self, cmd, 3, FALSE, NULL, NULL);
|
||||||
|
|
||||||
|
out:
|
||||||
|
/* Ignore errors */
|
||||||
|
@@ -10332,6 +10339,12 @@ mm_broadband_modem_get_current_charset (MMBroadbandModem *self)
|
||||||
|
return self->priv->modem_current_charset;
|
||||||
|
}
|
||||||
|
|
||||||
|
+MMFlowControl
|
||||||
|
+mm_broadband_modem_get_connected_flow_control (MMBroadbandModem *self)
|
||||||
|
+{
|
||||||
|
+ return self->priv->modem_flow_control;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
gchar *
|
||||||
|
mm_broadband_modem_create_device_identifier (MMBroadbandModem *self,
|
||||||
|
const gchar *ati,
|
||||||
|
@@ -10652,6 +10665,7 @@ mm_broadband_modem_init (MMBroadbandModem *self)
|
||||||
|
self->priv->current_sms_mem1_storage = MM_SMS_STORAGE_UNKNOWN;
|
||||||
|
self->priv->current_sms_mem2_storage = MM_SMS_STORAGE_UNKNOWN;
|
||||||
|
self->priv->sim_hot_swap_supported = FALSE;
|
||||||
|
+ self->priv->modem_flow_control = MM_FLOW_CONTROL_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
diff --git a/src/mm-broadband-modem.h b/src/mm-broadband-modem.h
|
||||||
|
index d6b55d9dd030..9ac4eabded65 100644
|
||||||
|
--- a/src/mm-broadband-modem.h
|
||||||
|
+++ b/src/mm-broadband-modem.h
|
||||||
|
@@ -24,6 +24,7 @@
|
||||||
|
|
||||||
|
#include <ModemManager.h>
|
||||||
|
|
||||||
|
+#include "mm-modem-helpers.h"
|
||||||
|
#include "mm-charsets.h"
|
||||||
|
#include "mm-base-modem.h"
|
||||||
|
|
||||||
|
@@ -109,6 +110,9 @@ gchar *mm_broadband_modem_take_and_convert_to_current_charset (MMBroadbandModem
|
||||||
|
|
||||||
|
MMModemCharset mm_broadband_modem_get_current_charset (MMBroadbandModem *self);
|
||||||
|
|
||||||
|
+/* Get the flow control setting to apply when connected, if any */
|
||||||
|
+MMFlowControl mm_broadband_modem_get_connected_flow_control (MMBroadbandModem *self);
|
||||||
|
+
|
||||||
|
/* Create a unique device identifier string using the ATI and ATI1 replies and some
|
||||||
|
* additional internal info */
|
||||||
|
gchar *mm_broadband_modem_create_device_identifier (MMBroadbandModem *self,
|
||||||
|
|
@ -15,6 +15,14 @@ PV = "1.7.0+git${SRCPV}"
|
||||||
SRC_URI = " \
|
SRC_URI = " \
|
||||||
git://anongit.freedesktop.org/git/ModemManager/ModemManager.git;protocol=https \
|
git://anongit.freedesktop.org/git/ModemManager/ModemManager.git;protocol=https \
|
||||||
file://0001-configure.ac-add-foreign-automake-option.patch \
|
file://0001-configure.ac-add-foreign-automake-option.patch \
|
||||||
|
file://0002-helpers-new-parser-for-AT-IFC.patch \
|
||||||
|
file://0003-broadband-modem-query-supported-flow-control-modes-b.patch \
|
||||||
|
file://0004-wavecom-ignore-custom-flow-control-handling.patch \
|
||||||
|
file://0005-telit-ignore-custom-flow-control-handling.patch \
|
||||||
|
file://0006-port-serial-new-internal-method-to-run-tcsetattr.patch \
|
||||||
|
file://0007-port-serial-new-method-to-explicitly-set-flow-contro.patch \
|
||||||
|
file://0008-port-serial-remove-all-default-flow-control-settings.patch \
|
||||||
|
file://0009-broadband-bearer-once-connected-set-flow-control-set.patch \
|
||||||
"
|
"
|
||||||
SRCREV = "34443289d97da5c95270c6071d140f4567047624"
|
SRCREV = "34443289d97da5c95270c6071d140f4567047624"
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue