The Quectel modems issues unsolicited strings in case of power related events. The UC15 uses +QIND: for the events, while M95 and MC60 uses descriptive strings. (UC15 also uses a string for normal power down).
Register listeners for these strings/codes. The handler emits an appropriate dbus signal, and closes down the modem if needed. --- doc/quectel-hardware-api.txt | 19 +++++ plugins/quectel.c | 148 ++++++++++++++++++++++++++++++++++- 2 files changed, 166 insertions(+), 1 deletion(-) diff --git a/doc/quectel-hardware-api.txt b/doc/quectel-hardware-api.txt index f54ea8c7..c9e93926 100644 --- a/doc/quectel-hardware-api.txt +++ b/doc/quectel-hardware-api.txt @@ -10,6 +10,25 @@ Methods array{string,variant} GetProperties Returns hardware properties for the modem object. See the properties section for available properties. +Signals PowerDown(string reason) + + This signal is emitted on gracefull shutdowns initiated + by the modem. + + Possible reasons: + "LowPower" The supply voltage is too low + "Normal" The PWRKEY pin was asserted + "HighPower" The supply voltage is too high + + PowerWarning(string reason) + + This signal is emitted when the modem detects its supply + voltage is close to its supported limits. + + Possible reasons: + "LowPower" The supply voltage is low + "HighPower" The supply voltage is high + Properties uint32 Voltage [readonly] Integer with the modem supply voltage in mV. diff --git a/plugins/quectel.c b/plugins/quectel.c index 3c1d49cd..3c74fc41 100644 --- a/plugins/quectel.c +++ b/plugins/quectel.c @@ -108,8 +108,122 @@ struct dbus_hw { gint voltage; }; +enum quectel_power_event { + LOW_POWER_DOWN = -2, + LOW_WARNING = -1, + NORMAL_POWER_DOWN = 0, + HIGH_WARNING = 1, + HIGH_POWER_DOWN = 2, +}; + static const char dbus_hw_interface[] = OFONO_SERVICE ".quectel.Hardware"; +static void close_serial(struct ofono_modem *modem); + +static void power_handle(struct ofono_modem *modem, + enum quectel_power_event event) +{ + DBusConnection *conn = ofono_dbus_get_connection(); + DBusMessage *signal; + DBusMessageIter iter; + const char *path = ofono_modem_get_path(modem); + const char *name; + const char *reason; + bool close; + + DBG("%p", modem); + + switch (event) { + case LOW_POWER_DOWN: + close = true; + name = "PowerDown"; + reason = "LowPower"; + break; + case LOW_WARNING: + close = false; + name = "PowerWarning"; + reason = "LowPower"; + break; + case NORMAL_POWER_DOWN: + close = true; + name = "PowerDown"; + reason = "Normal"; + break; + case HIGH_WARNING: + close = false; + name = "PowerWarning"; + reason = "HighPower"; + break; + case HIGH_POWER_DOWN: + close = true; + name = "PowerDown"; + reason = "HighPower"; + break; + default: + return; + } + + signal = dbus_message_new_signal(path, dbus_hw_interface, name); + if (signal) { + dbus_message_iter_init_append(signal, &iter); + dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, + &reason); + g_dbus_send_message(conn, signal); + } + + if (close) + close_serial(modem); +} + +static void qind_notify(GAtResult *result, void *user_data) +{ + struct dbus_hw *hw = user_data; + GAtResultIter iter; + enum quectel_power_event event; + const char *type; + + DBG("%p", hw->modem); + + g_at_result_iter_init(&iter, result); + g_at_result_iter_next(&iter, "+QIND:"); + + if (!g_at_result_iter_next_string(&iter, &type)) + return; + + if (!g_at_result_iter_next_number(&iter, &event)) + return; + + power_handle(hw->modem, event); +} + +static void power_notify(GAtResult *result, void *user_data) +{ + struct dbus_hw *hw = user_data; + GAtResultIter iter; + const char *event; + + DBG("%p", hw->modem); + + g_at_result_iter_init(&iter, result); + g_at_result_iter_next(&iter, NULL); + + if (!g_at_result_iter_next_unquoted_string(&iter, &event)) + return; + + DBG("event: %s", event); + + if (g_strcmp0(event, "UNDER_VOLTAGE POWER DOWN") == 0) + power_handle(hw->modem, LOW_POWER_DOWN); + else if (g_strcmp0(event, "UNDER_VOLTAGE WARNING") == 0) + power_handle(hw->modem, LOW_WARNING); + else if (g_strcmp0(event, "NORMAL POWER DOWN") == 0) + power_handle(hw->modem, NORMAL_POWER_DOWN); + else if (g_strcmp0(event, "OVER_VOLTAGE WARNING") == 0) + power_handle(hw->modem, HIGH_WARNING); + else if (g_strcmp0(event, "OVER_VOLTAGE POWER DOWN") == 0) + power_handle(hw->modem, HIGH_POWER_DOWN); +} + static void dbus_hw_reply_properties(struct dbus_hw *hw) { struct quectel_data *data = ofono_modem_get_data(hw->modem); @@ -206,6 +320,14 @@ static const GDBusMethodTable dbus_hw_methods[] = { {} }; +static const GDBusSignalTable dbus_hw_signals[] = { + { GDBUS_SIGNAL("PowerDown", + GDBUS_ARGS({ "reason", "s" })) }, + { GDBUS_SIGNAL("PowerWarning", + GDBUS_ARGS({ "reason", "s" })) }, + { } +}; + static void dbus_hw_cleanup(void *data) { struct dbus_hw *hw = data; @@ -222,6 +344,7 @@ static void dbus_hw_cleanup(void *data) static void dbus_hw_enable(struct ofono_modem *modem) { DBusConnection *conn = ofono_dbus_get_connection(); + struct quectel_data *data = ofono_modem_get_data(modem); const char *path = ofono_modem_get_path(modem); struct dbus_hw *hw; @@ -231,7 +354,7 @@ static void dbus_hw_enable(struct ofono_modem *modem) hw->modem = modem; if (!g_dbus_register_interface(conn, path, dbus_hw_interface, - dbus_hw_methods, NULL, NULL, + dbus_hw_methods, dbus_hw_signals, NULL, hw, dbus_hw_cleanup)) { ofono_error("Could not register %s interface under %s", dbus_hw_interface, path); @@ -239,6 +362,29 @@ static void dbus_hw_enable(struct ofono_modem *modem) return; } + g_at_chat_register(data->aux, "NORMAL POWER DOWN", power_notify, FALSE, + hw, NULL); + + switch (data->model) { + case QUECTEL_UC15: + g_at_chat_register(data->aux, "+QIND", qind_notify, FALSE, hw, + NULL); + break; + case QUECTEL_M95: + case QUECTEL_MC60: + g_at_chat_register(data->aux, "OVER_VOLTAGE POWER DOWN", + power_notify, FALSE, hw, NULL); + g_at_chat_register(data->aux, "UNDER_VOLTAGE POWER DOWN", + power_notify, FALSE, hw, NULL); + g_at_chat_register(data->aux, "OVER_VOLTAGE WARNING", + power_notify, FALSE, hw, NULL); + g_at_chat_register(data->aux, "UNDER_VOLTAGE WARNING", + power_notify, FALSE, hw, NULL); + break; + case QUECTEL_UNKNOWN: + break; + } + ofono_modem_add_interface(modem, dbus_hw_interface); } -- 2.22.0 _______________________________________________ ofono mailing list ofono@ofono.org https://lists.ofono.org/mailman/listinfo/ofono