The in-kernel implementation of gsm0710 causes deadlocks in the
kernel[1], so switch the default back to the user-space implementation
in ofono.

The change also removes the timeout-callback used to defer disabling the
n_gsm line discipline, as that is no longer needed[2]

To enable use of the kernel line discipline, add an udev env entry with
OFONO_QUECTEL_MUX="n_gsm".

[1] 
https://lore.kernel.org/lkml/4b2455c0-25ba-0187-6df6-c63b4ccc6...@geanix.com/
[2] 
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=7030082a7415d18e3befdf1f9ec05b3d5de98de4
---

Changes since v2:
 * keep kernel line discipline support
 * remove unrelated check in quectel_disable()
 * remove unrelated setting of AT+IFC=0,0
 * revert to using at_util_open_device()

 plugins/quectel.c | 202 ++++++++++++++++++++++++++++++++++++----------
 plugins/udevng.c  |   5 ++
 2 files changed, 166 insertions(+), 41 deletions(-)

diff --git a/plugins/quectel.c b/plugins/quectel.c
index f19065b2..8b2c0133 100644
--- a/plugins/quectel.c
+++ b/plugins/quectel.c
@@ -36,6 +36,7 @@
 #include <ell/ell.h>
 #include <gatchat.h>
 #include <gattty.h>
+#include <gatmux.h>
 
 #define OFONO_API_SUBJECT_TO_CHANGE
 #include <ofono.h>
@@ -95,7 +96,9 @@ struct quectel_data {
        bool sim_ready;
 
        /* used by quectel uart driver */
+       GIOChannel *device;
        GAtChat *uart;
+       GAtMux *mux;
        int mux_ready_count;
        int initial_ldisc;
        struct l_gpio_writer *gpio;
@@ -192,43 +195,48 @@ static void quectel_remove(struct ofono_modem *modem)
        g_at_chat_unref(data->aux);
        g_at_chat_unref(data->modem);
        g_at_chat_unref(data->uart);
+       g_at_mux_unref(data->mux);
+
+       if (data->device)
+               g_io_channel_unref(data->device);
+
        l_free(data);
 }
 
-static void close_mux_cb(struct l_timeout *timeout, void *user_data)
+static void close_mux(struct ofono_modem *modem)
+{
+       struct quectel_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       g_io_channel_unref(data->device);
+       data->device = NULL;
+
+       g_at_mux_unref(data->mux);
+       data->mux = NULL;
+}
+
+static void close_ngsm(struct ofono_modem *modem)
 {
-       struct ofono_modem *modem = user_data;
        struct quectel_data *data = ofono_modem_get_data(modem);
-       GIOChannel *device;
-       uint32_t gpio_value = 0;
-       ssize_t write_count;
        int fd;
 
        DBG("%p", modem);
 
-       device = g_at_chat_get_channel(data->uart);
-       fd = g_io_channel_unix_get_fd(device);
+       if (!data->device)
+               return;
+
+       fd = g_io_channel_unix_get_fd(data->device);
 
        /* restore initial tty line discipline */
        if (ioctl(fd, TIOCSETD, &data->initial_ldisc) < 0)
                ofono_warn("Failed to restore line discipline");
-
-       /* terminate gsm 0710 multiplexing on the modem side */
-       write_count = write(fd, gsm0710_terminate, sizeof(gsm0710_terminate));
-       if (write_count != sizeof(gsm0710_terminate))
-               ofono_warn("Failed to terminate gsm multiplexing");
-
-       g_at_chat_unref(data->uart);
-       data->uart = NULL;
-
-       l_timeout_remove(timeout);
-       l_gpio_writer_set(data->gpio, 1, &gpio_value);
-       ofono_modem_set_powered(modem, FALSE);
 }
 
 static void close_serial(struct ofono_modem *modem)
 {
        struct quectel_data *data = ofono_modem_get_data(modem);
+       uint32_t gpio_value = 0;
 
        DBG("%p", modem);
 
@@ -241,19 +249,16 @@ static void close_serial(struct ofono_modem *modem)
        g_at_chat_unref(data->modem);
        data->modem = NULL;
 
-       /*
-        * if gsm0710 multiplexing is used, the aux and modem file descriptors
-        * must be closed before closing the underlying serial device to avoid
-        * an old kernel dead-lock:
-        * https://lists.ofono.org/pipermail/ofono/2011-March/009405.html
-        *
-        * setup a timer to iterate the mainloop once to let gatchat close the
-        * virtual file descriptors unreferenced above
-        */
-       if (data->uart)
-               l_timeout_create_ms(1, close_mux_cb, modem, NULL);
+       g_at_chat_unref(data->uart);
+       data->uart = NULL;
+
+       if (data->mux)
+               close_mux(modem);
        else
-               ofono_modem_set_powered(modem, false);
+               close_ngsm(modem);
+
+       l_gpio_writer_set(data->gpio, 1, &gpio_value);
+       ofono_modem_set_powered(modem, FALSE);
 }
 
 static void dbus_hw_reply_properties(struct dbus_hw *hw)
@@ -793,6 +798,19 @@ static void cgmm_cb(int ok, GAtResult *result, void 
*user_data)
                        NULL);
 }
 
+static void setup_aux(struct ofono_modem *modem)
+{
+       struct quectel_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       g_at_chat_set_slave(data->modem, data->aux);
+       g_at_chat_send(data->aux, "ATE0; &C0; +CMEE=1; +QIURC=0", none_prefix,
+                       NULL, NULL, NULL);
+       g_at_chat_send(data->aux, "AT+CGMM", cgmm_prefix, cgmm_cb, modem,
+                       NULL);
+}
+
 static int open_ttys(struct ofono_modem *modem)
 {
        struct quectel_data *data = ofono_modem_get_data(modem);
@@ -812,16 +830,73 @@ static int open_ttys(struct ofono_modem *modem)
                return -EIO;
        }
 
-       g_at_chat_set_slave(data->modem, data->aux);
-
-       g_at_chat_send(data->aux, "ATE0; &C0; +CMEE=1; +QIURC=0", none_prefix,
-                       NULL, NULL, NULL);
-       g_at_chat_send(data->aux, "AT+CGMM", cgmm_prefix, cgmm_cb, modem,
-                       NULL);
+       setup_aux(modem);
 
        return -EINPROGRESS;
 }
 
+static GAtChat *create_chat(struct ofono_modem *modem, char *debug)
+{
+       struct quectel_data *data = ofono_modem_get_data(modem);
+       GIOChannel *channel;
+       GAtSyntax *syntax;
+       GAtChat *chat;
+
+       DBG("%p", modem);
+
+       channel = g_at_mux_create_channel(data->mux);
+       if (channel == NULL)
+               return NULL;
+
+       syntax = g_at_syntax_new_gsmv1();
+       chat = g_at_chat_new(channel, syntax);
+       g_at_syntax_unref(syntax);
+       g_io_channel_unref(channel);
+
+       if (chat == NULL)
+               return NULL;
+
+       if (getenv("OFONO_AT_DEBUG"))
+               g_at_chat_set_debug(chat, quectel_debug, debug);
+
+       return chat;
+}
+
+static void cmux_gatmux(struct ofono_modem *modem)
+{
+       struct quectel_data *data = ofono_modem_get_data(modem);
+
+       DBG("%p", modem);
+
+       data->mux = g_at_mux_new_gsm0710_basic(data->device, 127);
+       if (data->mux == NULL) {
+               ofono_error("failed to create gsm0710 mux");
+               close_serial(modem);
+               return;
+       }
+
+       if (getenv("OFONO_MUX_DEBUG"))
+               g_at_mux_set_debug(data->mux, quectel_debug, "Mux: ");
+
+       g_at_mux_start(data->mux);
+
+       data->modem = create_chat(modem, "Modem: ");
+       if (!data->modem) {
+               ofono_error("failed to create modem channel");
+               close_serial(modem);
+               return;
+       }
+
+       data->aux = create_chat(modem, "Aux: ");
+       if (!data->aux) {
+               ofono_error("failed to create aux channel");
+               close_serial(modem);
+               return;
+       }
+
+       setup_aux(modem);
+}
+
 static void mux_ready_cb(struct l_timeout *timeout, void *user_data)
 {
        struct ofono_modem *modem = user_data;
@@ -854,9 +929,8 @@ static void mux_ready_cb(struct l_timeout *timeout, void 
*user_data)
        g_at_chat_set_slave(data->uart, data->modem);
 }
 
-static void cmux_cb(gboolean ok, GAtResult *result, gpointer user_data)
+static void cmux_ngsm(struct ofono_modem *modem)
 {
-       struct ofono_modem *modem = user_data;
        struct quectel_data *data = ofono_modem_get_data(modem);
        struct gsm_config gsm_config;
        GIOChannel *device;
@@ -865,8 +939,7 @@ static void cmux_cb(gboolean ok, GAtResult *result, 
gpointer user_data)
 
        DBG("%p", modem);
 
-       device = g_at_chat_get_channel(data->uart);
-       fd = g_io_channel_unix_get_fd(device);
+       fd = g_io_channel_unix_get_fd(data->device);
 
        /* get initial line discipline to restore after use */
        if (ioctl(fd, TIOCGETD, &data->initial_ldisc) < 0) {
@@ -922,6 +995,39 @@ static void cmux_cb(gboolean ok, GAtResult *result, 
gpointer user_data)
        }
 }
 
+static void cmux_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+       struct ofono_modem *modem = user_data;
+       struct quectel_data *data = ofono_modem_get_data(modem);
+       const char *mux = ofono_modem_get_string(modem, "Mux");
+
+       DBG("%p", modem);
+
+       g_at_chat_unref(data->uart);
+       data->uart = NULL;
+
+       if (!ok) {
+               close_serial(modem);
+               return;
+       }
+
+       if (!mux)
+               mux = "internal";
+
+       if (strcmp(mux, "n_gsm") == 0) {
+               cmux_ngsm(modem);
+               return;
+       }
+
+       if (strcmp(mux, "internal") == 0) {
+               cmux_gatmux(modem);
+               return;
+       }
+
+       ofono_error("unsupported mux setting: '%s'", mux);
+       close_serial(modem);
+}
+
 static void ate_cb(int ok, GAtResult *result, void *user_data)
 {
        struct ofono_modem *modem = user_data;
@@ -979,6 +1085,8 @@ static int open_serial(struct ofono_modem *modem)
        struct quectel_data *data = ofono_modem_get_data(modem);
        const uint32_t gpio_value = 1;
        const char *rts_cts;
+       ssize_t written;
+       int fd;
 
        DBG("%p", modem);
 
@@ -998,6 +1106,18 @@ static int open_serial(struct ofono_modem *modem)
        if (data->uart == NULL)
                return -EINVAL;
 
+       data->device = g_at_chat_get_channel(data->uart);
+       g_io_channel_ref(data->device);
+
+       /*
+        * terminate gsm 0710 multiplexing on the modem side to make sure it
+        * responds to plain AT commands
+        * */
+       fd = g_io_channel_unix_get_fd(data->device);
+       written = write(fd, gsm0710_terminate, sizeof(gsm0710_terminate));
+       if (written != sizeof(gsm0710_terminate))
+               ofono_warn("Failed to terminate gsm multiplexing");
+
        if (data->gpio && !l_gpio_writer_set(data->gpio, 1, &gpio_value)) {
                close_serial(modem);
                return -EIO;
diff --git a/plugins/udevng.c b/plugins/udevng.c
index 40ed2b02..4a38621b 100644
--- a/plugins/udevng.c
+++ b/plugins/udevng.c
@@ -893,6 +893,11 @@ static gboolean setup_quectel_serial(struct modem_info 
*modem)
        if (value)
                ofono_modem_set_string(modem->modem, "GpioOffset", value);
 
+       value = udev_device_get_property_value(info->dev,
+                                               "OFONO_QUECTEL_MUX");
+       if (value)
+               ofono_modem_set_string(modem->modem, "Mux", value);
+
        value = udev_device_get_property_value(info->dev,
                                                "OFONO_QUECTEL_RTSCTS");
        ofono_modem_set_string(modem->modem, "RtsCts", value ? value : "off");
-- 
2.23.0
_______________________________________________
ofono mailing list -- ofono@ofono.org
To unsubscribe send an email to ofono-le...@ofono.org

Reply via email to