Skip to content

Commit

Permalink
qmimodem: get LTE default bearer APN from modem
Browse files Browse the repository at this point in the history
When an LTE modem registers with the network, a default bearer is
automatically established.  The APN used for this bearer is taken from
whatever default settings the modem has.

The LTE atom takes cares of setting up the default context/profile with
the APN to use.  From there, a default bearer will be established when
the modem registers with the network.  This results in a call to 'Get
LTE Attach Parameters' which tells us what APN the gateway negotiated
with us.

If we can't get the APN, we do what the AT driver does:  pretend the
bearer wasn't established.  This is a reasonable fallback, currently,
because connman can't handle zero-length APN's anyway; the previous
approach of setting the APN to 'automatic' breaks connman badly when it
needs to switch between LTE and non-LTE networks.
  • Loading branch information
Jonas Bonn authored and monich committed Feb 5, 2020
1 parent b0cd3e4 commit 9193d06
Showing 1 changed file with 153 additions and 9 deletions.
162 changes: 153 additions & 9 deletions ofono/drivers/qmimodem/gprs.c
Expand Up @@ -29,12 +29,16 @@

#include "qmi.h"
#include "nas.h"
#include "wds.h"

#include "src/common.h"
#include "qmimodem.h"

struct gprs_data {
struct qmi_device *dev;
struct qmi_service *nas;
struct qmi_service *wds;
unsigned int last_auto_context_id;
};

static bool extract_ss_info(struct qmi_result *result, int *status, int *tech)
Expand Down Expand Up @@ -64,8 +68,124 @@ static bool extract_ss_info(struct qmi_result *result, int *status, int *tech)
return true;
}

static void get_lte_attach_param_cb(struct qmi_result *result, void *user_data)
{
struct ofono_gprs *gprs = user_data;
struct gprs_data *data = ofono_gprs_get_data(gprs);
char *apn = NULL;
uint16_t error;
uint8_t iptype;

DBG("");

if (qmi_result_set_error(result, &error)) {
ofono_error("Failed to query LTE attach params: %hd", error);
goto noapn;
}

/* APN */
apn = qmi_result_get_string(result, 0x10);
if (!apn) {
DBG("Default profile has no APN setting");
goto noapn;
}

if (qmi_result_get_uint8(result, 0x11, &iptype))
ofono_info("LTE attach IP type: %hhd", iptype);

ofono_gprs_cid_activated(gprs, data->last_auto_context_id, apn);
g_free(apn);

return;

noapn:
data->last_auto_context_id = 0;
ofono_error("LTE bearer established but APN not set");
}

static void get_default_profile_cb(struct qmi_result *result, void *user_data)
{
struct ofono_gprs* gprs = user_data;
struct gprs_data *data = ofono_gprs_get_data(gprs);
uint16_t error;
uint8_t index;

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

data->last_auto_context_id = index;

/* Get LTE Attach Parameters */
if (qmi_service_send(data->wds, 0x85, NULL,
get_lte_attach_param_cb, gprs, NULL) > 0)
return;

error:
data->last_auto_context_id = 0;
ofono_error("LTE bearer established but APN not set");
}

/*
* Query the settings in effect on the default bearer. These may be
* implicit or may even be something other than requested as the gateway
* is allowed to override whatever was requested by the user.
*/
static void get_lte_attach_params(struct ofono_gprs* gprs)
{
struct gprs_data *data = ofono_gprs_get_data(gprs);
struct {
uint8_t type;
uint8_t family;
} __attribute((packed)) p = {
.type = 0, /* 3GPP */
.family = 0, /* embedded */
};
struct qmi_param *param;

DBG("");

if (data->last_auto_context_id != 0)
return; /* Established or in progress */

/* Set query in progress */
data->last_auto_context_id = -1;

/* First we query the default profile in order to find out which
* context the modem has activated.
*/
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(data->wds, 0x49, param,
get_default_profile_cb, gprs, NULL) > 0)
return;

qmi_param_free(param);

error:
ofono_warn("Unable to query LTE APN... will not activate context");
}

static int handle_ss_info(struct qmi_result *result, struct ofono_gprs *gprs)
{
struct gprs_data *data = ofono_gprs_get_data(gprs);
int status;
int tech;

Expand All @@ -74,17 +194,20 @@ static int handle_ss_info(struct qmi_result *result, struct ofono_gprs *gprs)
if (!extract_ss_info(result, &status, &tech))
return -1;

if (status == NETWORK_REGISTRATION_STATUS_REGISTERED)
if (status == NETWORK_REGISTRATION_STATUS_REGISTERED) {
if (tech == ACCESS_TECHNOLOGY_EUTRAN) {
/* On LTE we are effectively always attached; and
* the default bearer is established as soon as the
* network is joined.
* network is joined. We just need to query the
* parameters in effect on the default bearer and
* let the ofono core know about the activated
* context.
*/
/* FIXME: query default profile number and APN
* instead of assuming profile 1 and ""
*/
ofono_gprs_cid_activated(gprs, 1 , "automatic");
get_lte_attach_params(gprs);
}
} else {
data->last_auto_context_id = 0;
}

return status;
}
Expand Down Expand Up @@ -198,20 +321,20 @@ static void qmi_attached_status(struct ofono_gprs *gprs,
g_free(cbd);
}

static void create_nas_cb(struct qmi_service *service, void *user_data)
static void create_wds_cb(struct qmi_service *service, void *user_data)
{
struct ofono_gprs *gprs = user_data;
struct gprs_data *data = ofono_gprs_get_data(gprs);

DBG("");

if (!service) {
ofono_error("Failed to request NAS service");
ofono_error("Failed to request WDS service");
ofono_gprs_remove(gprs);
return;
}

data->nas = qmi_service_ref(service);
data->wds = qmi_service_ref(service);

/*
* First get the SS info - the modem may already be connected,
Expand All @@ -228,6 +351,25 @@ static void create_nas_cb(struct qmi_service *service, void *user_data)
ofono_gprs_register(gprs);
}

static void create_nas_cb(struct qmi_service *service, void *user_data)
{
struct ofono_gprs *gprs = user_data;
struct gprs_data *data = ofono_gprs_get_data(gprs);

DBG("");

if (!service) {
ofono_error("Failed to request NAS service");
ofono_gprs_remove(gprs);
return;
}

data->nas = qmi_service_ref(service);

qmi_service_create_shared(data->dev, QMI_SERVICE_WDS,
create_wds_cb, gprs, NULL);
}

static int qmi_gprs_probe(struct ofono_gprs *gprs,
unsigned int vendor, void *user_data)
{
Expand All @@ -240,6 +382,8 @@ static int qmi_gprs_probe(struct ofono_gprs *gprs,

ofono_gprs_set_data(gprs, data);

data->dev = device;

qmi_service_create_shared(device, QMI_SERVICE_NAS,
create_nas_cb, gprs, NULL);

Expand Down

0 comments on commit 9193d06

Please sign in to comment.