The in-kernel implementation of gsm0710 causes deadlocks in the kernel[1], so switch back to the user-space implementation in ofono.
[1] https://lore.kernel.org/lkml/4b2455c0-25ba-0187-6df6-c63b4ccc6...@geanix.com/ --- Changes since v1: * none plugins/quectel.c | 253 ++++++++++++++++++++++------------------------ 1 file changed, 121 insertions(+), 132 deletions(-) diff --git a/plugins/quectel.c b/plugins/quectel.c index f19065b2..002a2051 100644 --- a/plugins/quectel.c +++ b/plugins/quectel.c @@ -28,14 +28,10 @@ #include <stdbool.h> #include <unistd.h> -#include <sys/stat.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <linux/tty.h> -#include <linux/gsmmux.h> #include <ell/ell.h> #include <gatchat.h> #include <gattty.h> +#include <gatmux.h> #define OFONO_API_SUBJECT_TO_CHANGE #include <ofono.h> @@ -95,7 +91,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 +190,18 @@ 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); - l_free(data); -} - -static void close_mux_cb(struct l_timeout *timeout, void *user_data) -{ - 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); + g_at_mux_unref(data->mux); - device = g_at_chat_get_channel(data->uart); - fd = g_io_channel_unix_get_fd(device); + if (data->device) + g_io_channel_unref(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); + l_free(data); } 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 +214,19 @@ 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); - else - ofono_modem_set_powered(modem, false); + if (data->device) { + g_at_chat_unref(data->uart); + data->uart = NULL; + + g_io_channel_unref(data->device); + data->device = NULL; + + g_at_mux_unref(data->mux); + data->mux = NULL; + } + + 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 +766,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,114 +798,80 @@ 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 void mux_ready_cb(struct l_timeout *timeout, void *user_data) +static GAtChat *create_chat(struct ofono_modem *modem, char *debug) { - struct ofono_modem *modem = user_data; struct quectel_data *data = ofono_modem_get_data(modem); - struct stat st; - int ret; + GIOChannel *channel; + GAtSyntax *syntax; + GAtChat *chat; DBG("%p", modem); - /* check if the last (and thus all) virtual gsm tty's are created */ - ret = stat(ofono_modem_get_string(modem, "Modem"), &st); - if (ret < 0) { - if (data->mux_ready_count++ < 5) { - /* not ready yet; try again in 100 ms*/ - l_timeout_modify_ms(timeout, 100); - return; - } + channel = g_at_mux_create_channel(data->mux); + if (channel == NULL) + return NULL; - /* not ready after 500 ms; bail out */ - close_serial(modem); - return; - } + syntax = g_at_syntax_new_gsmv1(); + chat = g_at_chat_new(channel, syntax); + g_at_syntax_unref(syntax); + g_io_channel_unref(channel); - /* virtual gsm tty's are ready */ - l_timeout_remove(timeout); + if (chat == NULL) + return NULL; - if (open_ttys(modem) != -EINPROGRESS) - close_serial(modem); + if (getenv("OFONO_AT_DEBUG")) + g_at_chat_set_debug(chat, quectel_debug, debug); - g_at_chat_set_slave(data->uart, data->modem); + return chat; } 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); - struct gsm_config gsm_config; - GIOChannel *device; - int ldisc = N_GSM0710; - int fd; DBG("%p", modem); - device = g_at_chat_get_channel(data->uart); - fd = g_io_channel_unix_get_fd(device); + g_at_chat_unref(data->uart); + data->uart = NULL; - /* get initial line discipline to restore after use */ - if (ioctl(fd, TIOCGETD, &data->initial_ldisc) < 0) { - ofono_error("Failed to get current line discipline: %s", - strerror(errno)); + if (!ok) { close_serial(modem); return; } - /* enable gsm 0710 multiplexing line discipline */ - if (ioctl(fd, TIOCSETD, &ldisc) < 0) { - ofono_error("Failed to set multiplexer line discipline: %s", - strerror(errno)); + 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; } - /* get n_gsm configuration */ - if (ioctl(fd, GSMIOC_GETCONF, &gsm_config) < 0) { - ofono_error("Failed to get gsm config: %s", strerror(errno)); - close_serial(modem); - return; - } + if (getenv("OFONO_MUX_DEBUG")) + g_at_mux_set_debug(data->mux, quectel_debug, "Mux: "); - gsm_config.initiator = 1; /* cpu side is initiating multiplexing */ - gsm_config.encapsulation = 0; /* basic transparency encoding */ - gsm_config.mru = 127; /* 127 bytes rx mtu */ - gsm_config.mtu = 127; /* 127 bytes tx mtu */ - gsm_config.t1 = 10; /* 100 ms ack timer */ - gsm_config.n2 = 3; /* 3 retries */ - gsm_config.t2 = 30; /* 300 ms response timer */ - gsm_config.t3 = 10; /* 100 ms wake up response timer */ - gsm_config.i = 1; /* subset */ - - /* set the new configuration */ - if (ioctl(fd, GSMIOC_SETCONF, &gsm_config) < 0) { - ofono_error("Failed to set gsm config: %s", strerror(errno)); + 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; } - /* - * the kernel does not yet support mapping the underlying serial device - * to its virtual gsm ttys, so hard-code gsmtty1 gsmtty2 for now - */ - ofono_modem_set_string(modem, "Modem", "/dev/gsmtty1"); - ofono_modem_set_string(modem, "Aux", "/dev/gsmtty2"); - - /* wait for gsmtty devices to appear */ - if (!l_timeout_create_ms(100, mux_ready_cb, modem, NULL)) { + 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 ate_cb(int ok, GAtResult *result, void *user_data) @@ -950,8 +902,8 @@ static void init_cmd_cb(gboolean ok, GAtResult *result, void *user_data) g_at_chat_send(data->uart, "AT+IFC=2,2; E0", none_prefix, ate_cb, modem, NULL); else - g_at_chat_send(data->uart, "ATE0", none_prefix, ate_cb, modem, - NULL); + g_at_chat_send(data->uart, "AT+IFC=0,0; E0", none_prefix, + ate_cb, modem, NULL); l_timeout_remove(data->init_timeout); data->init_timeout = NULL; @@ -977,26 +929,58 @@ static void init_timeout_cb(struct l_timeout *timeout, void *user_data) static int open_serial(struct ofono_modem *modem) { struct quectel_data *data = ofono_modem_get_data(modem); + GHashTable *options; + GAtSyntax *syntax; const uint32_t gpio_value = 1; const char *rts_cts; + const char *device; + ssize_t written; + int fd; DBG("%p", modem); + device = ofono_modem_get_string(modem, "Device"); rts_cts = ofono_modem_get_string(modem, "RtsCts"); - data->uart = at_util_open_device(modem, "Device", quectel_debug, - "UART: ", - "Baud", "115200", - "Parity", "none", - "StopBits", "1", - "DataBits", "8", - "XonXoff", "off", - "Local", "on", - "Read", "on", - "RtsCts", rts_cts, - NULL); - if (data->uart == NULL) - return -EINVAL; + options = g_hash_table_new(g_str_hash, g_str_equal); + if (options == NULL) + return -ENOMEM; + + g_hash_table_insert(options, "Baud", "115200"); + g_hash_table_insert(options, "Parity", "none"); + g_hash_table_insert(options, "StopBits", "1"); + g_hash_table_insert(options, "DataBits", "8"); + g_hash_table_insert(options, "XonXoff", "off"); + g_hash_table_insert(options, "Local", "on"); + g_hash_table_insert(options, "Read", "on"); + g_hash_table_insert(options, "RtsCts", (char *)rts_cts); + + data->device = g_at_tty_open(device, options); + g_hash_table_destroy(options); + + if (data->device == NULL) + return -EIO; + + /* + * 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"); + + syntax = g_at_syntax_new_gsm_permissive(); + data->uart = g_at_chat_new(data->device, syntax); + g_at_syntax_unref(syntax); + + if (data->uart == NULL) { + close_serial(modem); + return -EIO; + } + + if (getenv("OFONO_AT_DEBUG")) + g_at_chat_set_debug(data->uart, quectel_debug, "UART: "); if (data->gpio && !l_gpio_writer_set(data->gpio, 1, &gpio_value)) { close_serial(modem); @@ -1057,6 +1041,11 @@ static int quectel_disable(struct ofono_modem *modem) DBG("%p", modem); + if (data->aux == NULL) { + close_serial(modem); + return 0; + } + g_at_chat_cancel_all(data->modem); g_at_chat_unregister_all(data->modem); -- 2.23.0 _______________________________________________ ofono mailing list -- ofono@ofono.org To unsubscribe send an email to ofono-le...@ofono.org