qmi: implement RAT selection

The QMI radio-settings atom was just a skeleton and did not even implement
the mandtory property TechnologyPreference.  As such, it probably should
never even have been registered for the modem.  Nonetheless, this patch
puts this mandatory property into place.

This is implemented via the 'Set System Selection' method by way of the
'mode' parameter.  This seems to best reflect the intention of the Ofono
API and works as expected when tested with a Quectel EC21.

Some notes:
i)  There is an alternative function called 'Set Technology Preference'
    which provides similar functionality.  This 'technology preference'
    is updated automatically when the 'system selection mode' is modified
    so everything seems to be in order.
ii) For the EC21, switching the underlying technology works seamlessly.
    There are indications, however, that some modems _might_ require a
    reset before changes take effect; that bridge will need to be crossed
    if reached.
This commit is contained in:
Jonas Bonn 2017-09-07 14:11:33 +02:00 committed by Denis Kenzior
parent b3e4837641
commit 4ac537394e
2 changed files with 125 additions and 1 deletions

View File

@ -35,6 +35,8 @@
#define QMI_NAS_SS_INFO_IND 36 /* Current serving system info indication */
#define QMI_NAS_GET_HOME_INFO 37 /* Get info about home network */
#define QMI_NAS_SET_SYSTEM_SELECTION_PREF 51
#define QMI_NAS_GET_SYSTEM_SELECTION_PREF 52
/* Set NAS state report conditions */
#define QMI_NAS_PARAM_REPORT_SIGNAL_STRENGTH 0x10
@ -164,4 +166,13 @@ struct qmi_nas_home_network {
char desc[0];
} __attribute__((__packed__));
#define QMI_NAS_RAT_MODE_PREF_ANY (-1)
#define QMI_NAS_RAT_MODE_PREF_GSM (1 << 2)
#define QMI_NAS_RAT_MODE_PREF_UMTS (1 << 3)
#define QMI_NAS_RAT_MODE_PREF_LTE (1 << 4)
#define QMI_NAS_PARAM_SYSTEM_SELECTION_PREF_MODE 0x11
#define QMI_NAS_RESULT_SYSTEM_SELECTION_PREF_MODE 0x11
int qmi_nas_rat_to_tech(uint8_t rat);

View File

@ -38,6 +38,118 @@ struct settings_data {
uint16_t minor;
};
static void get_system_selection_pref_cb(struct qmi_result *result,
void* user_data)
{
struct cb_data *cbd = user_data;
ofono_radio_settings_rat_mode_query_cb_t cb = cbd->cb;
enum ofono_radio_access_mode mode = OFONO_RADIO_ACCESS_MODE_ANY;
uint16_t pref;
DBG("");
if (qmi_result_set_error(result, NULL)) {
CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
return;
}
qmi_result_get_uint16(result,
QMI_NAS_RESULT_SYSTEM_SELECTION_PREF_MODE, &pref);
switch (pref) {
case QMI_NAS_RAT_MODE_PREF_GSM:
mode = OFONO_RADIO_ACCESS_MODE_GSM;
break;
case QMI_NAS_RAT_MODE_PREF_UMTS:
mode = OFONO_RADIO_ACCESS_MODE_UMTS;
break;
case QMI_NAS_RAT_MODE_PREF_LTE:
mode = OFONO_RADIO_ACCESS_MODE_LTE;
break;
}
CALLBACK_WITH_SUCCESS(cb, mode, cbd->data);
}
static void qmi_query_rat_mode(struct ofono_radio_settings *rs,
ofono_radio_settings_rat_mode_query_cb_t cb,
void *user_data)
{
struct settings_data *data = ofono_radio_settings_get_data(rs);
struct cb_data *cbd = cb_data_new(cb, user_data);
DBG("");
if (qmi_service_send(data->nas,
QMI_NAS_GET_SYSTEM_SELECTION_PREF, NULL,
get_system_selection_pref_cb, cbd, g_free) > 0)
return;
CALLBACK_WITH_FAILURE(cb, -1, data);
}
static void set_system_selection_pref_cb(struct qmi_result *result,
void* user_data)
{
struct cb_data *cbd = user_data;
ofono_radio_settings_rat_mode_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_set_rat_mode(struct ofono_radio_settings *rs,
enum ofono_radio_access_mode mode,
ofono_radio_settings_rat_mode_set_cb_t cb,
void *user_data)
{
struct settings_data *data = ofono_radio_settings_get_data(rs);
struct cb_data *cbd = cb_data_new(cb, user_data);
uint16_t pref = QMI_NAS_RAT_MODE_PREF_ANY;
struct qmi_param *param;
DBG("");
switch (mode) {
case OFONO_RADIO_ACCESS_MODE_ANY:
pref = QMI_NAS_RAT_MODE_PREF_ANY;
break;
case OFONO_RADIO_ACCESS_MODE_GSM:
pref = QMI_NAS_RAT_MODE_PREF_GSM;
break;
case OFONO_RADIO_ACCESS_MODE_UMTS:
pref = QMI_NAS_RAT_MODE_PREF_UMTS;
break;
case OFONO_RADIO_ACCESS_MODE_LTE:
pref = QMI_NAS_RAT_MODE_PREF_LTE;
break;
}
param = qmi_param_new();
if (!param) {
CALLBACK_WITH_FAILURE(cb, user_data);
return;
}
qmi_param_append_uint16(param, QMI_NAS_PARAM_SYSTEM_SELECTION_PREF_MODE,
pref);
if (qmi_service_send(data->nas,
QMI_NAS_SET_SYSTEM_SELECTION_PREF, param,
set_system_selection_pref_cb, cbd, g_free) > 0)
return;
qmi_param_free(param);
CALLBACK_WITH_FAILURE(cb, user_data);
g_free(cbd);
}
static void create_nas_cb(struct qmi_service *service, void *user_data)
{
struct ofono_radio_settings *rs = user_data;
@ -78,7 +190,6 @@ static int qmi_radio_settings_probe(struct ofono_radio_settings *rs,
create_nas_cb, rs, NULL);
return 0;
}
static void qmi_radio_settings_remove(struct ofono_radio_settings *rs)
@ -100,6 +211,8 @@ static struct ofono_radio_settings_driver driver = {
.name = "qmimodem",
.probe = qmi_radio_settings_probe,
.remove = qmi_radio_settings_remove,
.set_rat_mode = qmi_set_rat_mode,
.query_rat_mode = qmi_query_rat_mode,
};
void qmi_radio_settings_init(void)