diff options
author | Ivaylo Dimitrov <ivo.g.dimitrov.75@gmail.com> | 2024-02-14 08:32:08 +0200 |
---|---|---|
committer | Denis Kenzior <denkenz@gmail.com> | 2024-02-14 17:05:54 -0600 |
commit | 8af8136f61742be963b67b88f18e85c12509e0f9 (patch) | |
tree | 4114d04e058594159d26f935aeec81b2b1972b9e | |
parent | a1e492c39bae3762095c54b9d24b7e620047b5a7 (diff) | |
download | ofono-8af8136f61742be963b67b88f18e85c12509e0f9.tar.gz |
qmimodem: implement call-settings driver
-rw-r--r-- | Makefile.am | 3 | ||||
-rw-r--r-- | drivers/qmimodem/call-settings.c | 345 | ||||
-rw-r--r-- | drivers/qmimodem/voice.h | 45 | ||||
-rw-r--r-- | plugins/gobi.c | 5 |
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 = { |