mirror of git://git.sysmocom.de/ofono
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
This commit is contained in:
parent
ad73e590e2
commit
d0398b3965
|
@ -36,6 +36,7 @@
|
||||||
#include <ell/ell.h>
|
#include <ell/ell.h>
|
||||||
#include <gatchat.h>
|
#include <gatchat.h>
|
||||||
#include <gattty.h>
|
#include <gattty.h>
|
||||||
|
#include <gatmux.h>
|
||||||
|
|
||||||
#define OFONO_API_SUBJECT_TO_CHANGE
|
#define OFONO_API_SUBJECT_TO_CHANGE
|
||||||
#include <ofono.h>
|
#include <ofono.h>
|
||||||
|
@ -95,7 +96,9 @@ struct quectel_data {
|
||||||
bool sim_ready;
|
bool sim_ready;
|
||||||
|
|
||||||
/* used by quectel uart driver */
|
/* used by quectel uart driver */
|
||||||
|
GIOChannel *device;
|
||||||
GAtChat *uart;
|
GAtChat *uart;
|
||||||
|
GAtMux *mux;
|
||||||
int mux_ready_count;
|
int mux_ready_count;
|
||||||
int initial_ldisc;
|
int initial_ldisc;
|
||||||
struct l_gpio_writer *gpio;
|
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->aux);
|
||||||
g_at_chat_unref(data->modem);
|
g_at_chat_unref(data->modem);
|
||||||
g_at_chat_unref(data->uart);
|
g_at_chat_unref(data->uart);
|
||||||
|
g_at_mux_unref(data->mux);
|
||||||
|
|
||||||
|
if (data->device)
|
||||||
|
g_io_channel_unref(data->device);
|
||||||
|
|
||||||
l_free(data);
|
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);
|
struct quectel_data *data = ofono_modem_get_data(modem);
|
||||||
GIOChannel *device;
|
|
||||||
uint32_t gpio_value = 0;
|
|
||||||
ssize_t write_count;
|
|
||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
DBG("%p", modem);
|
DBG("%p", modem);
|
||||||
|
|
||||||
device = g_at_chat_get_channel(data->uart);
|
if (!data->device)
|
||||||
fd = g_io_channel_unix_get_fd(device);
|
return;
|
||||||
|
|
||||||
|
fd = g_io_channel_unix_get_fd(data->device);
|
||||||
|
|
||||||
/* restore initial tty line discipline */
|
/* restore initial tty line discipline */
|
||||||
if (ioctl(fd, TIOCSETD, &data->initial_ldisc) < 0)
|
if (ioctl(fd, TIOCSETD, &data->initial_ldisc) < 0)
|
||||||
ofono_warn("Failed to restore line discipline");
|
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)
|
static void close_serial(struct ofono_modem *modem)
|
||||||
{
|
{
|
||||||
struct quectel_data *data = ofono_modem_get_data(modem);
|
struct quectel_data *data = ofono_modem_get_data(modem);
|
||||||
|
uint32_t gpio_value = 0;
|
||||||
|
|
||||||
DBG("%p", modem);
|
DBG("%p", modem);
|
||||||
|
|
||||||
|
@ -241,19 +249,16 @@ static void close_serial(struct ofono_modem *modem)
|
||||||
g_at_chat_unref(data->modem);
|
g_at_chat_unref(data->modem);
|
||||||
data->modem = NULL;
|
data->modem = NULL;
|
||||||
|
|
||||||
/*
|
g_at_chat_unref(data->uart);
|
||||||
* if gsm0710 multiplexing is used, the aux and modem file descriptors
|
data->uart = NULL;
|
||||||
* must be closed before closing the underlying serial device to avoid
|
|
||||||
* an old kernel dead-lock:
|
if (data->mux)
|
||||||
* https://lists.ofono.org/pipermail/ofono/2011-March/009405.html
|
close_mux(modem);
|
||||||
*
|
|
||||||
* 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
|
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)
|
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);
|
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)
|
static int open_ttys(struct ofono_modem *modem)
|
||||||
{
|
{
|
||||||
struct quectel_data *data = ofono_modem_get_data(modem);
|
struct quectel_data *data = ofono_modem_get_data(modem);
|
||||||
|
@ -812,16 +830,73 @@ static int open_ttys(struct ofono_modem *modem)
|
||||||
return -EIO;
|
return -EIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
g_at_chat_set_slave(data->modem, data->aux);
|
setup_aux(modem);
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
return -EINPROGRESS;
|
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)
|
static void mux_ready_cb(struct l_timeout *timeout, void *user_data)
|
||||||
{
|
{
|
||||||
struct ofono_modem *modem = 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);
|
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 quectel_data *data = ofono_modem_get_data(modem);
|
||||||
struct gsm_config gsm_config;
|
struct gsm_config gsm_config;
|
||||||
GIOChannel *device;
|
|
||||||
int ldisc = N_GSM0710;
|
int ldisc = N_GSM0710;
|
||||||
int fd;
|
int fd;
|
||||||
|
|
||||||
DBG("%p", modem);
|
DBG("%p", modem);
|
||||||
|
|
||||||
device = g_at_chat_get_channel(data->uart);
|
fd = g_io_channel_unix_get_fd(data->device);
|
||||||
fd = g_io_channel_unix_get_fd(device);
|
|
||||||
|
|
||||||
/* get initial line discipline to restore after use */
|
/* get initial line discipline to restore after use */
|
||||||
if (ioctl(fd, TIOCGETD, &data->initial_ldisc) < 0) {
|
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)
|
static void ate_cb(int ok, GAtResult *result, void *user_data)
|
||||||
{
|
{
|
||||||
struct ofono_modem *modem = 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);
|
struct quectel_data *data = ofono_modem_get_data(modem);
|
||||||
const uint32_t gpio_value = 1;
|
const uint32_t gpio_value = 1;
|
||||||
const char *rts_cts;
|
const char *rts_cts;
|
||||||
|
ssize_t written;
|
||||||
|
int fd;
|
||||||
|
|
||||||
DBG("%p", modem);
|
DBG("%p", modem);
|
||||||
|
|
||||||
|
@ -998,6 +1105,18 @@ static int open_serial(struct ofono_modem *modem)
|
||||||
if (data->uart == NULL)
|
if (data->uart == NULL)
|
||||||
return -EINVAL;
|
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)) {
|
if (data->gpio && !l_gpio_writer_set(data->gpio, 1, &gpio_value)) {
|
||||||
close_serial(modem);
|
close_serial(modem);
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
Loading…
Reference in New Issue