Merge request !1294 from 'cbm/channels'
cellbroadcast: Allow to set channel list https://gitlab.freedesktop.org/mobile-broadband/ModemManager/-/merge_requests/1294 Closes #934
This commit is contained in:
@@ -87,5 +87,27 @@
|
||||
-->
|
||||
<property name="CellBroadcasts" type="ao" access="read" />
|
||||
|
||||
<!--
|
||||
SetChannels:
|
||||
@channels: The list of channels
|
||||
|
||||
Set the list of channels to receive Cell Broadcasts for.
|
||||
|
||||
Since: 1.24
|
||||
-->
|
||||
<method name="SetChannels">
|
||||
<arg name="channels" type="a(uu)" direction="in" />
|
||||
</method>
|
||||
|
||||
<!--
|
||||
Channels:
|
||||
|
||||
The list of channels that cell broadcast messages are
|
||||
received for.
|
||||
|
||||
Since: 1.24
|
||||
-->
|
||||
<property name="Channels" type="a(uu)" access="read" />
|
||||
|
||||
</interface>
|
||||
</node>
|
||||
|
||||
@@ -1280,6 +1280,77 @@ mm_common_build_oma_pending_network_initiated_sessions_default (void)
|
||||
return g_variant_builder_end (&builder);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/* MMModemCellbroadcastChannel array management */
|
||||
|
||||
GArray *
|
||||
mm_common_cell_broadcast_channels_variant_to_garray (GVariant *variant)
|
||||
{
|
||||
GArray *array = NULL;
|
||||
|
||||
if (variant) {
|
||||
GVariantIter iter;
|
||||
guint n;
|
||||
|
||||
g_variant_iter_init (&iter, variant);
|
||||
n = g_variant_iter_n_children (&iter);
|
||||
|
||||
if (n > 0) {
|
||||
MMCellBroadcastChannels channels;
|
||||
|
||||
array = g_array_sized_new (FALSE, FALSE, sizeof (MMCellBroadcastChannels), n);
|
||||
while (g_variant_iter_loop (&iter, "(uu)", &channels.start, &channels.end))
|
||||
g_array_append_val (array, channels);
|
||||
}
|
||||
}
|
||||
|
||||
/* If nothing set, fallback to empty */
|
||||
if (!array)
|
||||
array = g_array_new (FALSE, FALSE, sizeof (MMCellBroadcastChannels));
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
GVariant *
|
||||
mm_common_cell_broadcast_channels_array_to_variant (const MMCellBroadcastChannels *channels,
|
||||
guint n_sessions)
|
||||
{
|
||||
if (n_sessions > 0) {
|
||||
GVariantBuilder builder;
|
||||
guint i;
|
||||
|
||||
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(uu)"));
|
||||
|
||||
for (i = 0; i < n_sessions; i++)
|
||||
g_variant_builder_add_value (&builder,
|
||||
g_variant_new ("(uu)",
|
||||
((guint32)channels[i].start),
|
||||
((guint32)channels[i].end)));
|
||||
return g_variant_builder_end (&builder);
|
||||
}
|
||||
|
||||
return mm_common_build_cell_broadcast_channels_default ();
|
||||
}
|
||||
|
||||
GVariant *
|
||||
mm_common_cell_broadcast_channels_garray_to_variant (GArray *array)
|
||||
{
|
||||
if (array)
|
||||
return mm_common_cell_broadcast_channels_array_to_variant ((const MMCellBroadcastChannels *)array->data,
|
||||
array->len);
|
||||
|
||||
return mm_common_cell_broadcast_channels_array_to_variant (NULL, 0);
|
||||
}
|
||||
|
||||
GVariant *
|
||||
mm_common_build_cell_broadcast_channels_default (void)
|
||||
{
|
||||
GVariantBuilder builder;
|
||||
|
||||
g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(uu)"));
|
||||
return g_variant_builder_end (&builder);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
/* Common parsers */
|
||||
|
||||
|
||||
@@ -164,6 +164,13 @@ GVariant *mm_common_oma_pending_network_initiated_sessions_array_to_variant (co
|
||||
GVariant *mm_common_oma_pending_network_initiated_sessions_garray_to_variant (GArray *array);
|
||||
GVariant *mm_common_build_oma_pending_network_initiated_sessions_default (void);
|
||||
|
||||
/* MMModemCellbroadcastChannel array management */
|
||||
GArray *mm_common_cell_broadcast_channels_variant_to_garray (GVariant *variant);
|
||||
GVariant *mm_common_cell_broadcast_channels_array_to_variant (const MMCellBroadcastChannels *channels,
|
||||
guint n_sessions);
|
||||
GVariant *mm_common_cell_broadcast_channels_garray_to_variant (GArray *array);
|
||||
GVariant *mm_common_build_cell_broadcast_channels_default (void);
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
const gchar *mm_common_str_boolean (gboolean value);
|
||||
|
||||
@@ -80,4 +80,20 @@ struct _MMOmaPendingNetworkInitiatedSession {
|
||||
guint session_id;
|
||||
};
|
||||
|
||||
/**
|
||||
* MMCellBroadcastChannels:
|
||||
* @start: The first channel
|
||||
* @end: The last channel
|
||||
*
|
||||
* #MMCellBroadcastChannels is a simple struct specifying the start and end
|
||||
* of a channel interval.
|
||||
*
|
||||
* Since: 1.24
|
||||
*/
|
||||
typedef struct _MMCellBroadcastChannels MMCellBroadcastChannels;
|
||||
struct _MMCellBroadcastChannels {
|
||||
guint start;
|
||||
guint end;
|
||||
};
|
||||
|
||||
#endif /* _MM_HELPER_TYPES_H_ */
|
||||
|
||||
@@ -10499,6 +10499,65 @@ modem_cdma_register_in_network (MMIfaceModemCdma *_self,
|
||||
task);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Load currently active channels (CellBroadcast interface) */
|
||||
|
||||
static GArray *
|
||||
modem_cell_broadcast_load_channels_finish (MMIfaceModemCellBroadcast *self,
|
||||
GAsyncResult *res,
|
||||
GError **error)
|
||||
{
|
||||
return g_task_propagate_pointer (G_TASK (res), error);
|
||||
}
|
||||
|
||||
static void
|
||||
cscb_channels_format_check_ready (MMBroadbandModem *self,
|
||||
GAsyncResult *res,
|
||||
GTask *task)
|
||||
{
|
||||
const gchar *response;
|
||||
GError *error = NULL;
|
||||
GArray *result;
|
||||
|
||||
response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
|
||||
if (error) {
|
||||
g_task_return_error (task, error);
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Parse reply */
|
||||
result = mm_3gpp_parse_cscb_response (response, &error);
|
||||
if (!result) {
|
||||
g_task_return_error (task, error);
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
|
||||
g_task_return_pointer (task,
|
||||
result,
|
||||
(GDestroyNotify)g_array_unref);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
static void
|
||||
modem_cell_broadcast_load_channels (MMIfaceModemCellBroadcast *self,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GTask *task;
|
||||
|
||||
task = g_task_new (self, NULL, callback, user_data);
|
||||
|
||||
/* Load configured channels */
|
||||
mm_base_modem_at_command (MM_BASE_MODEM (self),
|
||||
"+CSCB?",
|
||||
3,
|
||||
TRUE,
|
||||
(GAsyncReadyCallback)cscb_channels_format_check_ready,
|
||||
task);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static gboolean
|
||||
@@ -10603,6 +10662,66 @@ modem_cell_broadcast_create_cbm (MMIfaceModemCellBroadcast *self)
|
||||
return mm_base_cbm_new (MM_BASE_MODEM (self));
|
||||
}
|
||||
|
||||
/***********************************************************************************/
|
||||
/* Get channels (CellBroadcast interface) */
|
||||
|
||||
static gboolean
|
||||
modem_cell_broadcast_set_channels_finish (MMIfaceModemCellBroadcast *self,
|
||||
GAsyncResult *res,
|
||||
GError **error)
|
||||
{
|
||||
return g_task_propagate_boolean (G_TASK (res), error);
|
||||
}
|
||||
|
||||
static void
|
||||
modem_cell_broadcast_set_channels_ready (MMBaseModem *self,
|
||||
GAsyncResult *res,
|
||||
GTask *task)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
if (!mm_base_modem_at_command_finish (self, res, &error))
|
||||
g_task_return_error (task, error);
|
||||
else
|
||||
g_task_return_boolean (task, TRUE);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
static void
|
||||
modem_cell_broadcast_set_channels (MMIfaceModemCellBroadcast *self,
|
||||
GArray *channels,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GTask *task;
|
||||
g_autoptr (GString) cmd = g_string_new ("+CSCB=0,\"");
|
||||
guint i;
|
||||
|
||||
task = g_task_new (self, NULL, callback, user_data);
|
||||
|
||||
for (i = 0; i < channels->len; i++) {
|
||||
MMCellBroadcastChannels ch = g_array_index (channels, MMCellBroadcastChannels, i);
|
||||
|
||||
if (i > 0)
|
||||
g_string_append_c (cmd, ',');
|
||||
|
||||
if (ch.start == ch.end)
|
||||
g_string_append_printf (cmd, "%u", ch.start);
|
||||
else
|
||||
g_string_append_printf (cmd, "%u-%u", ch.start, ch.end);
|
||||
}
|
||||
g_string_append (cmd, "\",\"\"");
|
||||
|
||||
mm_obj_dbg (self, "Setting channels...");
|
||||
mm_base_modem_at_command (
|
||||
MM_BASE_MODEM (self),
|
||||
cmd->str,
|
||||
3,
|
||||
FALSE,
|
||||
(GAsyncReadyCallback)modem_cell_broadcast_set_channels_ready,
|
||||
task);
|
||||
}
|
||||
|
||||
/*********************************************************/
|
||||
/* Check CellBroadcast support (CellBroadcast interface) */
|
||||
|
||||
@@ -14342,8 +14461,12 @@ iface_modem_cell_broadcast_init (MMIfaceModemCellBroadcastInterface *iface)
|
||||
iface->check_support_finish = modem_cell_broadcast_check_support_finish;
|
||||
iface->setup_unsolicited_events = modem_cell_broadcast_setup_unsolicited_events;
|
||||
iface->setup_unsolicited_events_finish = modem_cell_broadcast_setup_cleanup_unsolicited_events_finish;
|
||||
iface->load_channels = modem_cell_broadcast_load_channels;
|
||||
iface->load_channels_finish = modem_cell_broadcast_load_channels_finish;
|
||||
iface->cleanup_unsolicited_events = modem_cell_broadcast_cleanup_unsolicited_events;
|
||||
iface->cleanup_unsolicited_events_finish = modem_cell_broadcast_setup_cleanup_unsolicited_events_finish;
|
||||
iface->set_channels = modem_cell_broadcast_set_channels;
|
||||
iface->set_channels_finish = modem_cell_broadcast_set_channels_finish;
|
||||
iface->create_cbm = modem_cell_broadcast_create_cbm;
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,102 @@ mm_iface_modem_cell_broadcast_bind_simple_status (MMIfaceModemCellBroadcast *sel
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
|
||||
typedef struct {
|
||||
MmGdbusModemCellBroadcast *skeleton;
|
||||
GDBusMethodInvocation *invocation;
|
||||
MMIfaceModemCellBroadcast *self;
|
||||
GArray *channels;
|
||||
} HandleSetChannelsCellBroadcastContext;
|
||||
|
||||
static void
|
||||
handle_set_channels_context_free (HandleSetChannelsCellBroadcastContext *ctx)
|
||||
{
|
||||
g_object_unref (ctx->skeleton);
|
||||
g_object_unref (ctx->invocation);
|
||||
g_object_unref (ctx->self);
|
||||
g_array_unref (ctx->channels);
|
||||
g_slice_free (HandleSetChannelsCellBroadcastContext, ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
set_channels_ready (MMIfaceModemCellBroadcast *self,
|
||||
GAsyncResult *res,
|
||||
HandleSetChannelsCellBroadcastContext *ctx)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
if (!MM_IFACE_MODEM_CELL_BROADCAST_GET_IFACE (self)->set_channels_finish (self, res, &error))
|
||||
mm_dbus_method_invocation_take_error (ctx->invocation, error);
|
||||
else {
|
||||
mm_gdbus_modem_cell_broadcast_set_channels (ctx->skeleton,
|
||||
mm_common_cell_broadcast_channels_garray_to_variant (ctx->channels));
|
||||
mm_gdbus_modem_cell_broadcast_complete_set_channels (ctx->skeleton, ctx->invocation);
|
||||
}
|
||||
|
||||
handle_set_channels_context_free (ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_set_channels_auth_ready (MMBaseModem *self,
|
||||
GAsyncResult *res,
|
||||
HandleSetChannelsCellBroadcastContext *ctx)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
if (!mm_base_modem_authorize_finish (self, res, &error)) {
|
||||
mm_dbus_method_invocation_take_error (ctx->invocation, error);
|
||||
handle_set_channels_context_free (ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Validate channels (either number or range) */
|
||||
if (!mm_validate_cbs_channels (ctx->channels, &error)) {
|
||||
mm_dbus_method_invocation_return_gerror (ctx->invocation, error);
|
||||
handle_set_channels_context_free (ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Check if plugin implements it */
|
||||
if (!MM_IFACE_MODEM_CELL_BROADCAST_GET_IFACE (self)->set_channels ||
|
||||
!MM_IFACE_MODEM_CELL_BROADCAST_GET_IFACE (self)->set_channels_finish) {
|
||||
mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
|
||||
"Cannot set channels: not implemented");
|
||||
handle_set_channels_context_free (ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Request to change channels */
|
||||
mm_obj_info (self, "processing user request to set channels...");
|
||||
MM_IFACE_MODEM_CELL_BROADCAST_GET_IFACE (self)->set_channels (ctx->self,
|
||||
ctx->channels,
|
||||
(GAsyncReadyCallback)set_channels_ready,
|
||||
ctx);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
handle_set_channels (MmGdbusModemCellBroadcast *skeleton,
|
||||
GDBusMethodInvocation *invocation,
|
||||
GVariant *channels,
|
||||
MMIfaceModemCellBroadcast *self)
|
||||
{
|
||||
HandleSetChannelsCellBroadcastContext *ctx;
|
||||
|
||||
ctx = g_slice_new0 (HandleSetChannelsCellBroadcastContext);
|
||||
ctx->skeleton = g_object_ref (skeleton);
|
||||
ctx->invocation = g_object_ref (invocation);
|
||||
ctx->self = g_object_ref (self);
|
||||
ctx->channels = mm_common_cell_broadcast_channels_variant_to_garray (channels);
|
||||
mm_base_modem_authorize (MM_BASE_MODEM (self),
|
||||
invocation,
|
||||
MM_AUTHORIZATION_DEVICE_CONTROL,
|
||||
(GAsyncReadyCallback)handle_set_channels_auth_ready,
|
||||
ctx);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
MmGdbusModemCellBroadcast *skeleton;
|
||||
GDBusMethodInvocation *invocation;
|
||||
@@ -315,6 +411,12 @@ interface_initialization_step (GTask *task)
|
||||
|
||||
case INITIALIZATION_STEP_LAST:
|
||||
/* We are done without errors! */
|
||||
|
||||
/* Handle method invocations */
|
||||
g_signal_connect (ctx->skeleton,
|
||||
"handle-set-channels",
|
||||
G_CALLBACK (handle_set_channels),
|
||||
self);
|
||||
g_signal_connect (ctx->skeleton,
|
||||
"handle-delete",
|
||||
G_CALLBACK (handle_delete),
|
||||
@@ -425,6 +527,7 @@ typedef enum {
|
||||
ENABLING_STEP_FIRST,
|
||||
ENABLING_STEP_SETUP_UNSOLICITED_EVENTS,
|
||||
ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS,
|
||||
ENABLING_STEP_GET_CHANNELS,
|
||||
ENABLING_STEP_LAST
|
||||
} EnablingStep;
|
||||
|
||||
@@ -490,6 +593,31 @@ enable_unsolicited_events_ready (MMIfaceModemCellBroadcast *self,
|
||||
interface_enabling_step (task);
|
||||
}
|
||||
|
||||
static void
|
||||
load_channels_ready (MMIfaceModemCellBroadcast *self,
|
||||
GAsyncResult *res,
|
||||
GTask *task)
|
||||
{
|
||||
EnablingContext *ctx;
|
||||
g_autoptr (GError) error = NULL;
|
||||
g_autoptr (GArray) channels = NULL;
|
||||
|
||||
ctx = g_task_get_task_data (task);
|
||||
channels = MM_IFACE_MODEM_CELL_BROADCAST_GET_IFACE (self)->load_channels_finish (self, res, &error);
|
||||
if (channels) {
|
||||
mm_gdbus_modem_cell_broadcast_set_channels (
|
||||
ctx->skeleton,
|
||||
mm_common_cell_broadcast_channels_garray_to_variant (channels));
|
||||
} else {
|
||||
/* Not critical! */
|
||||
mm_obj_warn (self, "Couldn't load current channel list: %s", error->message);
|
||||
}
|
||||
|
||||
/* Go on with next step */
|
||||
ctx->step++;
|
||||
interface_enabling_step (task);
|
||||
}
|
||||
|
||||
static void
|
||||
interface_enabling_step (GTask *task)
|
||||
{
|
||||
@@ -552,6 +680,19 @@ interface_enabling_step (GTask *task)
|
||||
ctx->step++;
|
||||
/* fall through */
|
||||
|
||||
case ENABLING_STEP_GET_CHANNELS:
|
||||
/* Read current channel list */
|
||||
if (MM_IFACE_MODEM_CELL_BROADCAST_GET_IFACE (self)->load_channels &&
|
||||
MM_IFACE_MODEM_CELL_BROADCAST_GET_IFACE (self)->load_channels_finish) {
|
||||
MM_IFACE_MODEM_CELL_BROADCAST_GET_IFACE (self)->load_channels (
|
||||
self,
|
||||
(GAsyncReadyCallback)load_channels_ready,
|
||||
task);
|
||||
return;
|
||||
}
|
||||
ctx->step++;
|
||||
/* fall through */
|
||||
|
||||
case ENABLING_STEP_LAST:
|
||||
/* We are done without errors! */
|
||||
g_task_return_boolean (task, TRUE);
|
||||
|
||||
@@ -74,8 +74,26 @@ struct _MMIfaceModemCellBroadcastInterface {
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
|
||||
/* Asynchronous loading of channel list */
|
||||
GArray * (*load_channels_finish) (MMIfaceModemCellBroadcast *self,
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
|
||||
void (*load_channels) (MMIfaceModemCellBroadcast *self,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
|
||||
/* Create Cbm objects */
|
||||
MMBaseCbm * (* create_cbm) (MMIfaceModemCellBroadcast *self);
|
||||
|
||||
/* Set channel list */
|
||||
void (* set_channels) (MMIfaceModemCellBroadcast *self,
|
||||
GArray *channels,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
gboolean (* set_channels_finish) (MMIfaceModemCellBroadcast *self,
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
};
|
||||
|
||||
/* Initialize CellBroadcast interface (async) */
|
||||
|
||||
@@ -2936,6 +2936,110 @@ out:
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
#define CBS_MAX_CHANNEL G_MAXUINT16
|
||||
|
||||
GArray *
|
||||
mm_3gpp_parse_cscb_response (const char *response, GError **error)
|
||||
{
|
||||
g_autoptr(GRegex) r = NULL;
|
||||
g_autoptr(GMatchInfo) match_info = NULL;
|
||||
GError *inner_error = NULL;
|
||||
gsize len;
|
||||
g_autoptr (GArray) array = g_array_new (FALSE, FALSE, sizeof (MMCellBroadcastChannels));
|
||||
g_autofree char *str = NULL;
|
||||
g_auto (GStrv) intervals = NULL;
|
||||
int i;
|
||||
|
||||
len = strlen (response);
|
||||
if (!len) {
|
||||
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS, "empty channel list");
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* AT+CSCB=[0|1],"<channels>","<coding-scheme>"
|
||||
*/
|
||||
r = g_regex_new ("\\+CSCB:\\s*"
|
||||
"(\\d),\\s*" /* [0|1] */
|
||||
"\"([\\d,\\-]*)\"," /* channel list */
|
||||
"\"\"", /* encodings */
|
||||
G_REGEX_NEWLINE_CRLF,
|
||||
0,
|
||||
NULL);
|
||||
g_assert (r != NULL);
|
||||
|
||||
g_regex_match_full (r, response, -1, 0, 0, &match_info, &inner_error);
|
||||
if (inner_error)
|
||||
goto out;
|
||||
|
||||
if (!g_match_info_matches (match_info)) {
|
||||
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't match CSCB response");
|
||||
goto out;
|
||||
}
|
||||
|
||||
str = g_match_info_fetch (match_info, 1);
|
||||
if (!g_str_equal (str, "0")) {
|
||||
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
||||
"Couldn't match type of CSCB response: '%s'", str);
|
||||
goto out;
|
||||
}
|
||||
|
||||
str = g_match_info_fetch (match_info, 2);
|
||||
intervals = g_strsplit (str, ",", -1);
|
||||
for (i = 0; intervals[i]; i++) {
|
||||
gchar *interval_separator;
|
||||
|
||||
g_strstrip (intervals[i]);
|
||||
interval_separator = strstr (intervals[i], "-");
|
||||
if (interval_separator) {
|
||||
/* Add an interval */
|
||||
gchar *end;
|
||||
g_autofree gchar *start = NULL;
|
||||
MMCellBroadcastChannels channels;
|
||||
|
||||
start = g_strdup (intervals[i]);
|
||||
interval_separator = strstr (start, "-");
|
||||
*(interval_separator++) = '\0';
|
||||
end = interval_separator;
|
||||
|
||||
if (mm_get_uint_from_str (start, &channels.start) &&
|
||||
mm_get_uint_from_str (end, &channels.end) &&
|
||||
channels.start <= channels.end &&
|
||||
channels.end <= CBS_MAX_CHANNEL)
|
||||
g_array_append_val (array, channels);
|
||||
else {
|
||||
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
||||
"Couldn't parse CSCB interval '%s'", intervals[i]);
|
||||
goto out;
|
||||
}
|
||||
} else {
|
||||
guint channel;
|
||||
|
||||
/* Add single value */
|
||||
if (mm_get_uint_from_str (intervals[i], &channel)) {
|
||||
MMCellBroadcastChannels channels = {
|
||||
.start = channel,
|
||||
.end = channel
|
||||
};
|
||||
g_array_append_val (array, channels);
|
||||
} else {
|
||||
inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED,
|
||||
"Couldn't parse CSCB value '%s'", intervals[i]);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return g_steal_pointer (&array);
|
||||
|
||||
out:
|
||||
g_assert (inner_error);
|
||||
g_propagate_error (error, inner_error);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
/* CGATT helpers */
|
||||
|
||||
@@ -5200,6 +5304,38 @@ out:
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
gboolean
|
||||
mm_validate_cbs_channels (GArray *channels, GError **error)
|
||||
{
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < channels->len; i++) {
|
||||
MMCellBroadcastChannels ch = g_array_index (channels, MMCellBroadcastChannels, i);
|
||||
|
||||
if (ch.end < ch.start) {
|
||||
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
|
||||
"Invalid channels: End channel smaller than start channel");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (ch.start > CBS_MAX_CHANNEL) {
|
||||
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
|
||||
"Invalid channels: Start channel %u too large", ch.start);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (ch.end > CBS_MAX_CHANNEL) {
|
||||
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_INVALID_ARGS,
|
||||
"Invalid channels: End channel %u too large", ch.end);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
gboolean
|
||||
mm_sim_parse_cpol_query_response (const gchar *response,
|
||||
guint *out_index,
|
||||
|
||||
@@ -418,6 +418,8 @@ gboolean mm_3gpp_parse_ccwa_service_query_response (const gchar *response,
|
||||
gboolean *status,
|
||||
GError **error);
|
||||
|
||||
GArray *mm_3gpp_parse_cscb_response (const char *raw, GError **error);
|
||||
|
||||
/* CGATT helpers */
|
||||
gchar *mm_3gpp_build_cgatt_set_request (MMModem3gppPacketServiceState state);
|
||||
|
||||
@@ -550,6 +552,9 @@ gboolean mm_parse_supl_address (const gchar *supl,
|
||||
guint16 *out_port,
|
||||
GError **error);
|
||||
|
||||
gboolean mm_validate_cbs_channels (GArray *channels,
|
||||
GError **error);
|
||||
|
||||
/*****************************************************************************/
|
||||
/* SIM specific helpers and utilities */
|
||||
/*****************************************************************************/
|
||||
|
||||
@@ -4277,6 +4277,100 @@ test_ccwa_response (void)
|
||||
common_test_ccwa_response (test_ccwa[i].response, test_ccwa[i].expected_status, test_ccwa[i].expected_error);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Test +CSCB channel lists */
|
||||
|
||||
typedef struct {
|
||||
const gchar *response;
|
||||
const MMCellBroadcastChannels *channels;
|
||||
guint len;
|
||||
gboolean error;
|
||||
} TestCscb;
|
||||
|
||||
static void
|
||||
common_test_cscb_response (const gchar *response, TestCscb *expected)
|
||||
{
|
||||
GError *error = NULL;
|
||||
GArray *result;
|
||||
|
||||
g_debug ("Testing '%s'", response);
|
||||
result = mm_3gpp_parse_cscb_response (response, &error);
|
||||
|
||||
if (expected->error) {
|
||||
g_assert (!result);
|
||||
g_assert_nonnull (error);
|
||||
g_error_free (error);
|
||||
} else {
|
||||
guint i;
|
||||
|
||||
g_assert_no_error (error);
|
||||
g_assert (result);
|
||||
g_assert_cmpint (result->len, ==, expected->len);
|
||||
for (i = 0; i < expected->len; i++) {
|
||||
MMCellBroadcastChannels ch = g_array_index (result, MMCellBroadcastChannels, i);
|
||||
|
||||
g_assert_cmpuint (ch.start, ==, expected->channels[i].start);
|
||||
g_assert_cmpuint (ch.end, ==, expected->channels[i].end);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const MMCellBroadcastChannels cscb_one_channel[] = {
|
||||
{ .start = 0, .end = 0 },
|
||||
};
|
||||
|
||||
static const MMCellBroadcastChannels cscb_all_channels[] = {
|
||||
{ .start = 0, .end = 65535 },
|
||||
};
|
||||
|
||||
static const MMCellBroadcastChannels cscb_interval_channels[] = {
|
||||
{ .start = 0, .end = 1 },
|
||||
{ .start = 100, .end = 200 },
|
||||
};
|
||||
|
||||
static const MMCellBroadcastChannels cscb_dell5821e_channels[] = {
|
||||
{ .start = 4383, .end = 4383 },
|
||||
{ .start = 4400, .end = 4400 },
|
||||
{ .start = 4370, .end = 4370 },
|
||||
{ .start = 4371, .end = 4378 },
|
||||
{ .start = 4384, .end = 4391 },
|
||||
{ .start = 4396, .end = 4397 },
|
||||
};
|
||||
|
||||
static TestCscb test_cscb[] = {
|
||||
{ "\r\n+CSCB: 0, \"0\",\"\"\r\n\r\nOK\r\n", /* one channel */
|
||||
cscb_one_channel,
|
||||
G_N_ELEMENTS (cscb_all_channels),
|
||||
FALSE },
|
||||
{ "+CSCB: 0,\"0-65535\",\"\"\r\n", /* all channels */
|
||||
cscb_all_channels,
|
||||
G_N_ELEMENTS (cscb_all_channels),
|
||||
FALSE },
|
||||
{ "+CSCB: 0,\"0-1,100-200\",\"\"\r\n", /* intervals */
|
||||
cscb_interval_channels,
|
||||
G_N_ELEMENTS (cscb_interval_channels),
|
||||
FALSE },
|
||||
/* Dell 5821e/T77W968 defaults */
|
||||
{ "+CSCB: 0, \"4383,4400,4370,4371-4378,4384-4391,4396-4397\",\"\"",
|
||||
cscb_dell5821e_channels,
|
||||
G_N_ELEMENTS (cscb_dell5821e_channels),
|
||||
FALSE },
|
||||
{ "+CSCB: 0,\"0-\",\"\"\r\n", /* broken interval */
|
||||
NULL,
|
||||
0,
|
||||
TRUE },
|
||||
};
|
||||
|
||||
static void
|
||||
test_cscb_response (void)
|
||||
{
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < G_N_ELEMENTS (test_cscb); i++)
|
||||
common_test_cscb_response (test_cscb[i].response, &test_cscb[i]);
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Test +CLCC URCs */
|
||||
|
||||
@@ -4994,6 +5088,8 @@ int main (int argc, char **argv)
|
||||
g_test_suite_add (suite, TESTCASE (test_ccwa_indication, NULL));
|
||||
g_test_suite_add (suite, TESTCASE (test_ccwa_response, NULL));
|
||||
|
||||
g_test_suite_add (suite, TESTCASE (test_cscb_response, NULL));
|
||||
|
||||
g_test_suite_add (suite, TESTCASE (test_clcc_response_empty, NULL));
|
||||
g_test_suite_add (suite, TESTCASE (test_clcc_response_single, NULL));
|
||||
g_test_suite_add (suite, TESTCASE (test_clcc_response_single_long, NULL));
|
||||
|
||||
Reference in New Issue
Block a user