summaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
authorMartin Hundebøll <martin@geanix.com>2019-07-10 23:51:45 +0200
committerDenis Kenzior <denkenz@gmail.com>2019-07-12 10:50:51 -0500
commit2f58421b6431c27fd02a98bf5201ca45d5734662 (patch)
tree50919c9a60a2d6fb4b01f096fd3ce068e9034e2f /plugins
parent6a967b81c77c4364d0b6d568e494f96bbd3fffbe (diff)
downloadofono-2f58421b6431c27fd02a98bf5201ca45d5734662.tar.gz
quectel: support gpio to power on/off the modem
This adds support for configuring a gpio in udev to control the modem power. To enable gpio control, specify OFONO_QUECTEL_GPIO_CHIP and OFONO_QUECTEL_GPIO_OFFSET in the udev environment, for example: KERNEL=="ttymxc0", ENV{OFONO_DRIVER}="quectel", \ ENV{OFONO_QUECTEL_GPIO_CHIP}="gpiochip2", \ ENV{OFONO_QUECTEL_GPIO_OFFSET}="26"
Diffstat (limited to 'plugins')
-rw-r--r--plugins/quectel.c85
-rw-r--r--plugins/udevng.c11
2 files changed, 89 insertions, 7 deletions
diff --git a/plugins/quectel.c b/plugins/quectel.c
index 8ed1fc07..b657b534 100644
--- a/plugins/quectel.c
+++ b/plugins/quectel.c
@@ -74,6 +74,7 @@ struct quectel_data {
GAtChat *uart;
int mux_ready_count;
int initial_ldisc;
+ struct l_gpio_writer *gpio;
};
static void quectel_debug(const char *str, void *user_data)
@@ -83,6 +84,43 @@ static void quectel_debug(const char *str, void *user_data)
ofono_info("%s%s", prefix, str);
}
+static int quectel_probe_gpio(struct ofono_modem *modem)
+{
+ struct quectel_data *data = ofono_modem_get_data(modem);
+ struct l_gpio_chip *gpiochip;
+ uint32_t offset;
+ const char *chip_name, *offset_str;
+ uint32_t value = 0;
+
+ DBG("%p", modem);
+
+ chip_name = ofono_modem_get_string(modem, "GpioChip");
+ if (!chip_name)
+ return 0;
+
+ offset_str = ofono_modem_get_string(modem, "GpioOffset");
+ if (!offset_str)
+ return -EINVAL;
+
+ offset = strtoul(offset_str, NULL, 0);
+ if (!offset)
+ return -EINVAL;
+
+ gpiochip = l_gpio_chip_new(chip_name);
+ if (!gpiochip)
+ return -ENODEV;
+
+ data->gpio = l_gpio_writer_new(gpiochip, "ofono", 1, &offset,
+ &value);
+
+ l_gpio_chip_free(gpiochip);
+
+ if (!data->gpio)
+ return -EIO;
+
+ return 0;
+}
+
static int quectel_probe(struct ofono_modem *modem)
{
struct quectel_data *data;
@@ -93,7 +131,7 @@ static int quectel_probe(struct ofono_modem *modem)
ofono_modem_set_data(modem, data);
- return 0;
+ return quectel_probe_gpio(modem);
}
static void quectel_remove(struct ofono_modem *modem)
@@ -106,6 +144,7 @@ static void quectel_remove(struct ofono_modem *modem)
g_at_chat_unregister(data->aux, data->cpin_ready);
ofono_modem_set_data(modem, NULL);
+ l_gpio_writer_free(data->gpio);
g_at_chat_unref(data->aux);
g_at_chat_unref(data->modem);
g_at_chat_unref(data->uart);
@@ -117,6 +156,7 @@ 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;
@@ -138,6 +178,7 @@ static void close_mux_cb(struct l_timeout *timeout, void *user_data)
data->uart = NULL;
l_timeout_remove(timeout);
+ l_gpio_writer_set(data->gpio, 1, &gpio_value);
ofono_modem_set_powered(modem, FALSE);
}
@@ -393,9 +434,22 @@ static void cmux_cb(gboolean ok, GAtResult *result, gpointer user_data)
}
}
+static void ate_cb(int ok, GAtResult *result, void *user_data)
+{
+ struct ofono_modem *modem = user_data;
+ struct quectel_data *data = ofono_modem_get_data(modem);
+
+ DBG("%p", modem);
+
+ g_at_chat_set_wakeup_command(data->uart, NULL, 0, 0);
+ g_at_chat_send(data->uart, "AT+CMUX=0,0,5,127,10,3,30,10,2", NULL,
+ cmux_cb, modem, NULL);
+}
+
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;
DBG("%p", modem);
@@ -416,12 +470,31 @@ static int open_serial(struct ofono_modem *modem)
if (data->uart == NULL)
return -EINVAL;
- g_at_chat_send(data->uart, "ATE0", none_prefix, NULL, NULL,
- NULL);
+ if (data->gpio && !l_gpio_writer_set(data->gpio, 1, &gpio_value)) {
+ close_serial(modem);
+ return -EIO;
+ }
- /* setup multiplexing */
- g_at_chat_send(data->uart, "AT+CMUX=0,0,5,127,10,3,30,10,2", NULL,
- cmux_cb, modem, NULL);
+ /*
+ * there are three different power-up scenarios:
+ *
+ * 1) the gpio has just been toggled on, so the modem is not ready
+ * until it prints RDY
+ *
+ * 2) the modem has been on for a while and ready to respond to
+ * commands, so there will be no RDY notification
+ *
+ * 3) either of the previous to scenarious is the case, but the modem
+ * UART is not configured to a fixed bitrate. In this case it needs
+ * a few 'AT' bytes to detect the host UART bitrate, but the RDY is
+ * lost.
+ *
+ * the wakeup command feature is (mis)used to support all three
+ * scenarious by sending AT commands until the modem responds with OK,
+ * at which point the modem is ready.
+ */
+ g_at_chat_set_wakeup_command(data->uart, "AT\r", 500, 10000);
+ g_at_chat_send(data->uart, "ATE0", none_prefix, ate_cb, modem, NULL);
return -EINPROGRESS;
}
diff --git a/plugins/udevng.c b/plugins/udevng.c
index f8817a2f..35354a16 100644
--- a/plugins/udevng.c
+++ b/plugins/udevng.c
@@ -883,8 +883,17 @@ static gboolean setup_quectel_serial(struct modem_info *modem)
const char *value;
value = udev_device_get_property_value(info->dev,
- "OFONO_QUECTEL_RTSCTS");
+ "OFONO_QUECTEL_GPIO_CHIP");
+ if (value)
+ ofono_modem_set_string(modem->modem, "GpioChip", value);
+ value = udev_device_get_property_value(info->dev,
+ "OFONO_QUECTEL_GPIO_OFFSET");
+ if (value)
+ ofono_modem_set_string(modem->modem, "GpioOffset", value);
+
+ value = udev_device_get_property_value(info->dev,
+ "OFONO_QUECTEL_RTSCTS");
ofono_modem_set_string(modem->modem, "RtsCts", value ? value : "off");
ofono_modem_set_string(modem->modem, "Device", info->devnode);