qmi: add LTE atom driver

This patch adds an LTE atom for QMI modems.

This atom sets the APN that the LTE default bearer should use when
establishing its PDP context.  This APN needs to be set on the 'default'
profile so the atom queries which profile is the default and resets
it before allowing the APN to be set.

Once configured, the default profile settings are used when the
modem connects to the network; for this reason, the LTE atom needs
to be instantiated in post_sim, before the modem is set online.
This commit is contained in:
Jonas Bonn 2018-03-05 13:31:01 +01:00 committed by Denis Kenzior
parent dca9071ffe
commit f2e38a6b42
4 changed files with 270 additions and 0 deletions

View File

@ -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

264
drivers/qmimodem/lte.c Normal file
View File

@ -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 <config.h>
#endif
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <glib.h>
#include <ofono/modem.h>
#include <ofono/gprs-context.h>
#include <ofono/log.h>
#include <ofono/lte.h>
#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);
}

View File

@ -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();

View File

@ -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);