summaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
authorMartin Hundebøll <martin@geanix.com>2019-10-07 23:39:59 +0200
committerDenis Kenzior <denkenz@gmail.com>2019-10-11 12:19:20 -0500
commitd0398b3965105b981502fca68afe4f244bc8d443 (patch)
tree7ff93caf840dc947d433c8122435176022be6ce6 /plugins
parentad73e590e28ab20067304e698b29727d0ded2bf8 (diff)
downloadofono-d0398b3965105b981502fca68afe4f244bc8d443.tar.gz
quectel: support both internal and n_gsm muxes
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-c63b4ccc6a6e@geanix.com/ [2] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=7030082a7415d18e3befdf1f9ec05b3d5de98de4
Diffstat (limited to 'plugins')
-rw-r--r--plugins/quectel.c203
1 files changed, 161 insertions, 42 deletions
diff --git a/plugins/quectel.c b/plugins/quectel.c
index f19065b2..5d3ad470 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,19 +929,16 @@ 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;
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);
+ 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 +994,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 +1084,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 +1105,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;