aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIvaylo Dimitrov <ivo.g.dimitrov.75@gmail.com>2024-02-14 08:32:08 +0200
committerDenis Kenzior <denkenz@gmail.com>2024-02-14 17:05:54 -0600
commit8af8136f61742be963b67b88f18e85c12509e0f9 (patch)
tree4114d04e058594159d26f935aeec81b2b1972b9e
parenta1e492c39bae3762095c54b9d24b7e620047b5a7 (diff)
downloadofono-8af8136f61742be963b67b88f18e85c12509e0f9.tar.gz
qmimodem: implement call-settings driver
-rw-r--r--Makefile.am3
-rw-r--r--drivers/qmimodem/call-settings.c345
-rw-r--r--drivers/qmimodem/voice.h45
-rw-r--r--plugins/gobi.c5
4 files changed, 391 insertions, 7 deletions
diff --git a/Makefile.am b/Makefile.am
index a82340eb2..d8766e08a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -386,7 +386,8 @@ builtin_sources += $(qmi_sources) \
drivers/qmimodem/lte.c \
drivers/qmimodem/radio-settings.c \
drivers/qmimodem/location-reporting.c \
- drivers/qmimodem/netmon.c
+ drivers/qmimodem/netmon.c \
+ drivers/qmimodem/call-settings.c
builtin_modules += gobi
builtin_sources += plugins/gobi.c
diff --git a/drivers/qmimodem/call-settings.c b/drivers/qmimodem/call-settings.c
new file mode 100644
index 000000000..07b9d543b
--- /dev/null
+++ b/drivers/qmimodem/call-settings.c
@@ -0,0 +1,345 @@
+/*
+ * oFono - Open Source Telephony
+ *
+ * Copyright (C) 2024 Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/call-settings.h>
+
+#include "qmi.h"
+#include "voice.h"
+#include "util.h"
+
+struct call_settings_data {
+ struct qmi_service *voice;
+ uint16_t sups_ind_id;
+};
+
+static void query_status(struct ofono_call_settings *cs, uint16_t message,
+ qmi_result_func_t fn,
+ ofono_call_settings_status_cb_t cb, void *data)
+{
+ struct call_settings_data *csd = ofono_call_settings_get_data(cs);
+ struct cb_data *cbd = cb_data_new(cb, data);
+
+ DBG("");
+
+ if (!csd)
+ goto error;
+
+ if (qmi_service_send(csd->voice, message, NULL, fn, cbd, g_free) > 0)
+ return;
+error:
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(cb, -1, data);
+}
+
+static void cw_cb(struct qmi_result *result, void *user_data)
+{
+ struct cb_data *cbd = user_data;
+ ofono_call_settings_status_cb_t cb = cbd->cb;
+ uint8_t status;
+
+ DBG("");
+
+ if (qmi_result_set_error(result, NULL) ||
+ !qmi_result_get_uint8(result, 0x10, &status)) {
+ CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
+ return;
+ }
+
+ CALLBACK_WITH_SUCCESS(cb, status, cbd->data);
+}
+
+static void qmi_cw_query(struct ofono_call_settings *cs, int cls,
+ ofono_call_settings_status_cb_t cb, void *data)
+{
+ query_status(cs, QMI_VOICE_GET_CALL_WAITING, cw_cb, cb, data);
+}
+
+static void status_cb(struct qmi_result *result, void *user_data)
+{
+ struct cb_data *cbd = user_data;
+ ofono_call_settings_status_cb_t cb = cbd->cb;
+ uint16_t len;
+ const struct __attribute__((__packed__)) {
+ uint8_t active;
+ uint8_t provisioned;
+ } *rsp;
+
+ DBG("");
+
+ if (qmi_result_set_error(result, NULL))
+ goto error;
+
+ rsp = qmi_result_get(result, 0x10, &len);
+ if (!rsp || len != sizeof(*rsp))
+ goto error;
+
+ CALLBACK_WITH_SUCCESS(cb, rsp->provisioned, cbd->data);
+ return;
+error:
+ CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
+}
+
+static void qmi_clip_query(struct ofono_call_settings *cs,
+ ofono_call_settings_status_cb_t cb, void *data)
+{
+ query_status(cs, QMI_VOICE_GET_CLIP, status_cb, cb, data);
+}
+
+static void qmi_colp_query(struct ofono_call_settings *cs,
+ ofono_call_settings_status_cb_t cb, void *data)
+{
+ query_status(cs, QMI_VOICE_GET_COLP, status_cb, cb, data);
+}
+
+static void qmi_colr_query(struct ofono_call_settings *cs,
+ ofono_call_settings_status_cb_t cb, void *data)
+{
+ query_status(cs, QMI_VOICE_GET_COLR, status_cb, cb, data);
+}
+
+static void qmi_cnap_query(struct ofono_call_settings *cs,
+ ofono_call_settings_status_cb_t cb, void *data)
+{
+ query_status(cs, QMI_VOICE_GET_CNAP, status_cb, cb, data);
+}
+
+static void clir_cb(struct qmi_result *result, void *user_data)
+{
+ struct cb_data *cbd = user_data;
+ ofono_call_settings_clir_cb_t cb = cbd->cb;
+ uint16_t len;
+ const struct __attribute__((__packed__)) {
+ uint8_t active;
+ uint8_t provisioned;
+ } *rsp;
+ uint8_t network;
+
+ DBG("");
+
+ if (qmi_result_set_error(result, NULL))
+ goto error;
+
+ rsp = qmi_result_get(result, 0x10, &len);
+ if (!rsp || len != sizeof(*rsp))
+ goto error;
+
+ network = rsp->provisioned;
+ /* we do not have UNKNOWN */
+ if (network > 1)
+ network++;
+
+ CALLBACK_WITH_SUCCESS(cb, rsp->active, network, cbd->data);
+ return;
+error:
+ CALLBACK_WITH_FAILURE(cb, -1, -1, cbd->data);
+}
+
+static void qmi_clir_query(struct ofono_call_settings *cs,
+ ofono_call_settings_clir_cb_t cb, void *data)
+{
+ struct call_settings_data *csd = ofono_call_settings_get_data(cs);
+ struct cb_data *cbd = cb_data_new(cb, data);
+
+ DBG("");
+
+ if (!csd)
+ goto error;
+
+ if (qmi_service_send(csd->voice, QMI_VOICE_GET_CLIR, NULL,
+ clir_cb, cbd, g_free) > 0)
+ return;
+error:
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(cb, -1, -1, data);
+}
+
+static void cw_set_cb(struct qmi_result *result, void *user_data)
+{
+ struct cb_data *cbd = user_data;
+ ofono_call_settings_set_cb_t cb = cbd->cb;
+
+ DBG("");
+
+ if (qmi_result_set_error(result, NULL)) {
+ CALLBACK_WITH_FAILURE(cb, cbd->data);
+ return;
+ }
+
+ CALLBACK_WITH_SUCCESS(cb, cbd->data);
+}
+
+static void qmi_cw_set(struct ofono_call_settings *cs, int mode, int cls,
+ ofono_call_settings_set_cb_t cb, void *data)
+{
+ struct call_settings_data *csd = ofono_call_settings_get_data(cs);
+ struct cb_data *cbd = cb_data_new(cb, data);
+ struct qmi_param *param;
+ struct __attribute__((__packed__)) {
+ uint8_t service;
+ uint8_t reason;
+ } ssd;
+
+ DBG("");
+
+ if (!csd)
+ goto error;
+
+ param = qmi_param_new();
+
+ ssd.service = mode ? QMI_VOICE_SS_ACTION_ACTIVATE :
+ QMI_VOICE_SS_ACTION_DEACTIVATE;
+ ssd.reason = QMI_VOICE_SS_RSN_CALL_WAITING;
+
+ qmi_param_append(param, 0x01, sizeof(ssd), &ssd);
+
+ if (cls != 7 /* BEARER_CLASS_DEFAULT */)
+ qmi_param_append_uint8(param, 0x10, cls);
+
+ if (qmi_service_send(csd->voice, QMI_VOICE_SET_SUPS_SERVICE, param,
+ cw_set_cb, cbd, g_free) > 0)
+ return;
+
+ qmi_param_free(param);
+error:
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(cb, data);
+}
+
+/* call_settings API lacks change notifications, just log data for now */
+static void sups_ind(struct qmi_result *result, void *user_data)
+{
+ const struct __attribute__((__packed__)) {
+ uint8_t request;
+ uint8_t mod_cc;
+ } *info;
+ const struct __attribute__((__packed__)) {
+ uint8_t active;
+ uint8_t status;
+ } *clir;
+ const struct __attribute__((__packed__)) {
+ uint8_t active;
+ uint8_t provisioned;
+ } *clip;
+ uint8_t cls;
+ uint8_t reason;
+ uint8_t data;
+ uint16_t len;
+
+ DBG("SS notification");
+
+ info = qmi_result_get(result, 0x01, &len);
+ if (info && len == sizeof(*info))
+ DBG("request %d", info->request);
+
+ if (qmi_result_get_uint8(result, 0x10, &cls))
+ DBG("class %d", cls);
+
+ if (qmi_result_get_uint8(result, 0x11, &reason))
+ DBG("reason %d", reason);
+
+ if (qmi_result_get_uint8(result, 0x19, &data))
+ DBG("data %d", data);
+
+ clir = qmi_result_get(result, 0x1c, &len);
+ if (clir && len == sizeof(*clir))
+ DBG("clir active %d, status %d", clir->active, clir->status);
+
+ clip = qmi_result_get(result, 0x1d, &len);
+ if (clip && len == sizeof(*clip))
+ DBG("clip active %d, provisioned %d",
+ clip->active, clip->provisioned);
+}
+
+static void create_voice_cb(struct qmi_service *service, void *user_data)
+{
+ struct ofono_call_settings *cs = user_data;
+ struct call_settings_data *csd = ofono_call_settings_get_data(cs);
+
+ DBG("");
+
+ if (!service) {
+ ofono_error("Failed to request Voice service");
+ ofono_call_settings_remove(cs);
+ return;
+ }
+
+ csd->voice = qmi_service_ref(service);
+
+ csd->sups_ind_id = qmi_service_register(csd->voice, QMI_VOICE_SUPS_IND,
+ sups_ind, cs, NULL);
+
+ ofono_call_settings_register(cs);
+}
+
+static int qmi_call_settings_probe(struct ofono_call_settings *cs,
+ unsigned int vendor, void *user_data)
+{
+ struct qmi_device *device = user_data;
+ struct call_settings_data *csd;
+
+ DBG("");
+
+ csd = g_new0(struct call_settings_data, 1);
+
+ ofono_call_settings_set_data(cs, csd);
+
+ qmi_service_create_shared(device, QMI_SERVICE_VOICE,
+ create_voice_cb, cs, NULL);
+
+ return 0;
+}
+
+static void qmi_call_settings_remove(struct ofono_call_settings *cs)
+{
+ struct call_settings_data *csd = ofono_call_settings_get_data(cs);
+
+ DBG("");
+
+ ofono_call_settings_set_data(cs, NULL);
+
+ if (csd->voice) {
+ qmi_service_unregister(csd->voice, csd->sups_ind_id);
+ qmi_service_unref(csd->voice);
+ }
+
+ g_free(csd);
+}
+
+static const struct ofono_call_settings_driver driver = {
+ .probe = qmi_call_settings_probe,
+ .remove = qmi_call_settings_remove,
+ .clip_query = qmi_clip_query,
+ .colp_query = qmi_colp_query,
+ .colr_query = qmi_colr_query,
+ .cnap_query = qmi_cnap_query,
+ .clir_query = qmi_clir_query,
+ .cw_query = qmi_cw_query,
+ .cw_set = qmi_cw_set
+};
+
+OFONO_ATOM_DRIVER_BUILTIN(call_settings, qmimodem, &driver)
diff --git a/drivers/qmimodem/voice.h b/drivers/qmimodem/voice.h
index ca1464910..7755d2e07 100644
--- a/drivers/qmimodem/voice.h
+++ b/drivers/qmimodem/voice.h
@@ -48,11 +48,19 @@ enum qmi_ussd_user_required {
/* QMI service voice. Using an enum to prevent doublicated entries */
enum voice_commands {
- QMI_VOICE_CANCEL_USSD = 0x3c,
- QMI_VOICE_USSD_RELEASE_IND = 0x3d,
- QMI_VOICE_USSD_IND = 0x3e,
- QMI_VOICE_SUPS_IND = 0x42,
- QMI_VOICE_ASYNC_ORIG_USSD = 0x43,
+ QMI_VOICE_SUPS_NOTIFICATION_IND = 0x32,
+ QMI_VOICE_SET_SUPS_SERVICE = 0x33,
+ QMI_VOICE_GET_CALL_WAITING = 0x34,
+ QMI_VOICE_GET_CLIP = 0x36,
+ QMI_VOICE_GET_CLIR = 0x37,
+ QMI_VOICE_CANCEL_USSD = 0x3c,
+ QMI_VOICE_USSD_RELEASE_IND = 0x3d,
+ QMI_VOICE_USSD_IND = 0x3e,
+ QMI_VOICE_SUPS_IND = 0x42,
+ QMI_VOICE_ASYNC_ORIG_USSD = 0x43,
+ QMI_VOICE_GET_COLP = 0x4b,
+ QMI_VOICE_GET_COLR = 0x4c,
+ QMI_VOICE_GET_CNAP = 0x4d
};
struct qmi_ussd_data {
@@ -60,3 +68,30 @@ struct qmi_ussd_data {
uint8_t length;
uint8_t data[0];
} __attribute__((__packed__));
+
+enum qmi_ss_action {
+ QMI_VOICE_SS_ACTION_ACTIVATE = 0x01,
+ QMI_VOICE_SS_ACTION_DEACTIVATE = 0x02,
+ QMI_VOICE_SS_ACTION_REGISTER = 0x03,
+ QMI_VOICE_SS_ACTION_ERASE = 0x04
+};
+
+enum qmi_ss_reason {
+ QMI_VOICE_SS_RSN_FWD_UNCONDITIONAL = 0x01,
+ QMI_VOICE_SS_RSN_FWD_MOBILE_BUSY = 0x02,
+ QMI_VOICE_SS_RSN_FWD_NO_REPLY = 0x03,
+ QMI_VOICE_SS_RSN_FWD_UNREACHABLE = 0x04,
+ QMI_VOICE_SS_RSN_FWD_ALL = 0x05,
+ QMI_VOICE_SS_RSN_FWD_ALL_CONDITIONAL = 0x06,
+ QMI_VOICE_SS_RSN_ALL_OUTGOING = 0x07,
+ QMI_VOICE_SS_RSN_OUT_INT = 0x08,
+ QMI_VOICE_SS_RSN_OUT_INT_EXT_TO_HOME = 0x09,
+ QMI_VOICE_SS_RSN_ALL_IN = 0x0A,
+ QMI_VOICE_SS_RSN_IN_ROAMING = 0x0B,
+ QMI_VOICE_SS_RSN_BAR_ALL = 0x0C,
+ QMI_VOICE_SS_RSN_BAR_ALL_OUTGOING = 0x0D,
+ QMI_VOICE_SS_RSN_BAR_ALL_IN = 0x0E,
+ QMI_VOICE_SS_RSN_CALL_WAITING = 0x0F,
+ QMI_VOICE_SS_RSN_CLIP = 0x10,
+ QMI_VOICE_SS_RSN_CLIR = 0x11
+};
diff --git a/plugins/gobi.c b/plugins/gobi.c
index 21f70d1ee..a55001660 100644
--- a/plugins/gobi.c
+++ b/plugins/gobi.c
@@ -33,6 +33,7 @@
#define OFONO_API_SUBJECT_TO_CHANGE
#include <ofono/plugin.h>
#include <ofono/modem.h>
+#include <ofono/call-settings.h>
#include <ofono/devinfo.h>
#include <ofono/netreg.h>
#include <ofono/netmon.h>
@@ -753,8 +754,10 @@ static void gobi_post_online(struct ofono_modem *modem)
ofono_netmon_create(modem, 0, "qmimodem", data->device);
}
- if (data->features & GOBI_VOICE)
+ if (data->features & GOBI_VOICE) {
ofono_ussd_create(modem, 0, "qmimodem", data->device);
+ ofono_call_settings_create(modem, 0, "qmimodem", data->device);
+ }
}
static struct ofono_modem_driver gobi_driver = {