diff --git a/Makefile.am b/Makefile.am index d790a215..a921bd2b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -234,6 +234,7 @@ builtin_sources += $(qmi_sources) \ drivers/qmimodem/ussd.c \ drivers/qmimodem/gprs.c \ drivers/qmimodem/gprs-context.c \ + drivers/qmimodem/lte.c \ drivers/qmimodem/radio-settings.c \ drivers/qmimodem/location-reporting.c \ drivers/qmimodem/netmon.c diff --git a/drivers/qmimodem/lte.c b/drivers/qmimodem/lte.c new file mode 100644 index 00000000..9a149289 --- /dev/null +++ b/drivers/qmimodem/lte.c @@ -0,0 +1,264 @@ +/* + * + * oFono - Open Source Telephony + * + * Copyright (C) 2018 Jonas Bonn. All rights reserved. + * Copyright (C) 2018 Norrbonn AB. All rights reserved. + * Copyright (C) 2018 Data Respons ASA. All rights reserved. + * + * 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 +#endif + +#define _GNU_SOURCE +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "qmi.h" +#include "wds.h" + +#include "qmimodem.h" + +struct lte_data { + struct qmi_service *wds; + uint8_t default_profile; +}; + +static void modify_profile_cb(struct qmi_result *result, void *user_data) +{ + struct cb_data *cbd = user_data; + ofono_lte_cb_t cb = cbd->cb; + uint16_t error; + + DBG(""); + + if (qmi_result_set_error(result, &error)) { + DBG("Failed to modify profile: %d", error); + CALLBACK_WITH_FAILURE(cb, cbd->data); + return; + } + + CALLBACK_WITH_SUCCESS(cb, cbd->data); +} + +static void qmimodem_lte_set_default_attach_info(const struct ofono_lte *lte, + const struct ofono_lte_default_attach_info *info, + ofono_lte_cb_t cb, void *data) +{ + struct lte_data *ldd = ofono_lte_get_data(lte); + struct cb_data *cbd = cb_data_new(cb, data); + struct qmi_param* param; + struct { + uint8_t type; + uint8_t index; + } __attribute__((packed)) p = { + .type = 0, /* 3GPP */ + }; + + DBG(""); + + p.index = ldd->default_profile; + + param = qmi_param_new(); + if (!param) + goto error; + + /* Profile selector */ + qmi_param_append(param, 0x01, sizeof(p), &p); + + /* WDS APN Name */ + qmi_param_append(param, QMI_WDS_PARAM_APN, + strlen(info->apn), info->apn); + + /* Modify profile */ + if (qmi_service_send(ldd->wds, 0x28, param, + modify_profile_cb, cbd, g_free) > 0) + return; + + qmi_param_free(param); + +error: + CALLBACK_WITH_FAILURE(cb, cbd->data); +} + +static void reset_profile_cb(struct qmi_result *result, void *user_data) +{ + struct ofono_lte *lte = user_data; + uint16_t error; + + DBG(""); + + if (qmi_result_set_error(result, &error)) + ofono_error("Reset profile error: %hd", error); + + ofono_lte_register(lte); +} + +static void get_default_profile_cb(struct qmi_result *result, void *user_data) +{ + struct ofono_lte *lte = user_data; + struct lte_data *ldd = ofono_lte_get_data(lte); + uint16_t error; + uint8_t index; + struct qmi_param *param; + struct { + uint8_t type; + uint8_t index; + } __attribute__((packed)) p = { + .type = 0, /* 3GPP */ + }; + + DBG(""); + + if (qmi_result_set_error(result, &error)) { + ofono_error("Get default profile error: %hd", error); + goto error; + } + + /* Profile index */ + if (!qmi_result_get_uint8(result, 0x01, &index)) { + ofono_error("Failed query default profile"); + goto error; + } + + DBG("Default profile index: %hhd", index); + + ldd->default_profile = index; + + p.index = index; + + param = qmi_param_new(); + if (!param) + goto error; + + /* Profile selector */ + qmi_param_append(param, 0x01, sizeof(p), &p); + + /* Reset profile */ + if (qmi_service_send(ldd->wds, 0x4b, param, + reset_profile_cb, lte, NULL) > 0) + return; + + qmi_param_free(param); + +error: + ofono_error("Failed to reset profile %hhd", index); + ofono_lte_remove(lte); +} + +static void create_wds_cb(struct qmi_service *service, void *user_data) +{ + struct ofono_lte *lte = user_data; + struct lte_data *ldd = ofono_lte_get_data(lte); + struct qmi_param *param; + struct { + uint8_t type; + uint8_t family; + } __attribute((packed)) p = { + .type = 0, /* 3GPP */ + .family = 0, /* embedded */ + }; + + DBG(""); + + if (!service) { + ofono_error("Failed to request WDS service"); + ofono_lte_remove(lte); + return; + } + + ldd->wds = qmi_service_ref(service); + + /* Query the default profile */ + param = qmi_param_new(); + if (!param) + goto error; + + /* Profile type */ + qmi_param_append(param, 0x1, sizeof(p), &p); + + /* Get default profile */ + if (qmi_service_send(ldd->wds, 0x49, param, + get_default_profile_cb, lte, NULL) > 0) + return; + + qmi_param_free(param); + +error: + ofono_error("Failed to query default profile"); + ofono_lte_register(lte); +} + +static int qmimodem_lte_probe(struct ofono_lte *lte, void *data) +{ + struct qmi_device *device = data; + struct lte_data *ldd; + + DBG("qmimodem lte probe"); + + ldd = g_try_new0(struct lte_data, 1); + if (!ldd) + return -ENOMEM; + + ofono_lte_set_data(lte, ldd); + + qmi_service_create_shared(device, QMI_SERVICE_WDS, + create_wds_cb, lte, NULL); + + return 0; +} + +static void qmimodem_lte_remove(struct ofono_lte *lte) +{ + struct lte_data *ldd = ofono_lte_get_data(lte); + + DBG(""); + + ofono_lte_set_data(lte, NULL); + + qmi_service_unregister_all(ldd->wds); + + qmi_service_unref(ldd->wds); + + g_free(ldd); +} + +static struct ofono_lte_driver driver = { + .name = "qmimodem", + .probe = qmimodem_lte_probe, + .remove = qmimodem_lte_remove, + .set_default_attach_info = qmimodem_lte_set_default_attach_info, +}; + +void qmi_lte_init(void) +{ + ofono_lte_driver_register(&driver); +} + +void qmi_lte_exit(void) +{ + ofono_lte_driver_unregister(&driver); +} diff --git a/drivers/qmimodem/qmimodem.c b/drivers/qmimodem/qmimodem.c index b10ce28c..11e68f2e 100644 --- a/drivers/qmimodem/qmimodem.c +++ b/drivers/qmimodem/qmimodem.c @@ -39,6 +39,7 @@ static int qmimodem_init(void) qmi_ussd_init(); qmi_gprs_init(); qmi_gprs_context_init(); + qmi_lte_init(); qmi_radio_settings_init(); qmi_location_reporting_init(); qmi_netmon_init(); @@ -51,6 +52,7 @@ static void qmimodem_exit(void) qmi_netmon_exit(); qmi_location_reporting_exit(); qmi_radio_settings_exit(); + qmi_lte_exit(); qmi_gprs_context_exit(); qmi_gprs_exit(); qmi_ussd_exit(); diff --git a/drivers/qmimodem/qmimodem.h b/drivers/qmimodem/qmimodem.h index 4b0fad3f..eeb1375a 100644 --- a/drivers/qmimodem/qmimodem.h +++ b/drivers/qmimodem/qmimodem.h @@ -48,6 +48,9 @@ extern void qmi_gprs_exit(void); extern void qmi_gprs_context_init(void); extern void qmi_gprs_context_exit(void); +extern void qmi_lte_init(void); +extern void qmi_lte_exit(void); + extern void qmi_radio_settings_init(void); extern void qmi_radio_settings_exit(void);