quectel: implement dbus signals for modem power notifications

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.
This commit is contained in:
Martin Hundebøll 2019-07-19 14:39:56 +02:00 committed by Denis Kenzior
parent b57fff4843
commit 1107d3cbc3
1 changed files with 145 additions and 1 deletions

View File

@ -108,6 +108,14 @@ struct dbus_hw {
int32_t 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 quectel_debug(const char *str, void *user_data)
@ -335,6 +343,110 @@ static DBusMessage *dbus_hw_get_properties(DBusConnection *conn,
return NULL;
}
static void voltage_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 = "VoltageLow";
break;
case LOW_WARNING:
close = false;
name = "PowerWarning";
reason = "VoltageLow";
break;
case NORMAL_POWER_DOWN:
close = true;
name = "PowerDown";
reason = "Normal";
break;
case HIGH_WARNING:
close = false;
name = "PowerWarning";
reason = "VoltageHigh";
break;
case HIGH_POWER_DOWN:
close = true;
name = "PowerDown";
reason = "VoltageHigh";
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;
voltage_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)
voltage_handle(hw->modem, LOW_POWER_DOWN);
else if (g_strcmp0(event, "UNDER_VOLTAGE WARNING") == 0)
voltage_handle(hw->modem, LOW_WARNING);
else if (g_strcmp0(event, "NORMAL POWER DOWN") == 0)
voltage_handle(hw->modem, NORMAL_POWER_DOWN);
else if (g_strcmp0(event, "OVER_VOLTAGE WARNING") == 0)
voltage_handle(hw->modem, HIGH_WARNING);
else if (g_strcmp0(event, "OVER_VOLTAGE POWER DOWN") == 0)
voltage_handle(hw->modem, HIGH_POWER_DOWN);
}
static const GDBusMethodTable dbus_hw_methods[] = {
{ GDBUS_ASYNC_METHOD("GetProperties",
NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
@ -342,6 +454,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;
@ -358,6 +478,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;
@ -367,7 +488,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);
@ -375,6 +496,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);
}