diff options
-rw-r--r-- | introspection/org.freedesktop.ModemManager1.Modem.CellBroadcast.xml | 22 | ||||
-rw-r--r-- | libmm-glib/mm-common-helpers.c | 71 | ||||
-rw-r--r-- | libmm-glib/mm-common-helpers.h | 7 | ||||
-rw-r--r-- | libmm-glib/mm-helper-types.h | 16 | ||||
-rw-r--r-- | src/mm-broadband-modem.c | 123 | ||||
-rw-r--r-- | src/mm-iface-modem-cell-broadcast.c | 141 | ||||
-rw-r--r-- | src/mm-iface-modem-cell-broadcast.h | 18 | ||||
-rw-r--r-- | src/mm-modem-helpers.c | 136 | ||||
-rw-r--r-- | src/mm-modem-helpers.h | 5 | ||||
-rw-r--r-- | src/tests/test-modem-helpers.c | 96 |
10 files changed, 635 insertions, 0 deletions
diff --git a/introspection/org.freedesktop.ModemManager1.Modem.CellBroadcast.xml b/introspection/org.freedesktop.ModemManager1.Modem.CellBroadcast.xml index 9e8584a1..572e094c 100644 --- a/introspection/org.freedesktop.ModemManager1.Modem.CellBroadcast.xml +++ b/introspection/org.freedesktop.ModemManager1.Modem.CellBroadcast.xml @@ -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> diff --git a/libmm-glib/mm-common-helpers.c b/libmm-glib/mm-common-helpers.c index 72007ab5..e249a2fc 100644 --- a/libmm-glib/mm-common-helpers.c +++ b/libmm-glib/mm-common-helpers.c @@ -1281,6 +1281,77 @@ mm_common_build_oma_pending_network_initiated_sessions_default (void) } /******************************************************************************/ +/* 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 */ /* Expecting input as: diff --git a/libmm-glib/mm-common-helpers.h b/libmm-glib/mm-common-helpers.h index 6ff89e21..bea220e0 100644 --- a/libmm-glib/mm-common-helpers.h +++ b/libmm-glib/mm-common-helpers.h @@ -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); diff --git a/libmm-glib/mm-helper-types.h b/libmm-glib/mm-helper-types.h index 0d9de269..cc0d3a28 100644 --- a/libmm-glib/mm-helper-types.h +++ b/libmm-glib/mm-helper-types.h @@ -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_ */ diff --git a/src/mm-broadband-modem.c b/src/mm-broadband-modem.c index 4b13fd09..5e026449 100644 --- a/src/mm-broadband-modem.c +++ b/src/mm-broadband-modem.c @@ -10500,6 +10500,65 @@ modem_cdma_register_in_network (MMIfaceModemCdma *_self, } /*****************************************************************************/ +/* 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 modem_cell_broadcast_setup_cleanup_unsolicited_events_finish (MMIfaceModemCellBroadcast *self, @@ -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; } diff --git a/src/mm-iface-modem-cell-broadcast.c b/src/mm-iface-modem-cell-broadcast.c index 8e5af67c..f543d8f9 100644 --- a/src/mm-iface-modem-cell-broadcast.c +++ b/src/mm-iface-modem-cell-broadcast.c @@ -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; @@ -491,6 +594,31 @@ enable_unsolicited_events_ready (MMIfaceModemCellBroadcast *self, } 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) { MMIfaceModemCellBroadcast *self; @@ -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); diff --git a/src/mm-iface-modem-cell-broadcast.h b/src/mm-iface-modem-cell-broadcast.h index 5990b865..32170b7b 100644 --- a/src/mm-iface-modem-cell-broadcast.h +++ b/src/mm-iface-modem-cell-broadcast.h @@ -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) */ diff --git a/src/mm-modem-helpers.c b/src/mm-modem-helpers.c index 772a2de8..4f0c6248 100644 --- a/src/mm-modem-helpers.c +++ b/src/mm-modem-helpers.c @@ -2937,6 +2937,110 @@ out: } /*************************************************************************/ + +#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 */ gchar * @@ -5201,6 +5305,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, gchar **out_operator_code, diff --git a/src/mm-modem-helpers.h b/src/mm-modem-helpers.h index f4b2ae8b..133c7006 100644 --- a/src/mm-modem-helpers.h +++ b/src/mm-modem-helpers.h @@ -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 */ /*****************************************************************************/ diff --git a/src/tests/test-modem-helpers.c b/src/tests/test-modem-helpers.c index 538aedfe..6692e56d 100644 --- a/src/tests/test-modem-helpers.c +++ b/src/tests/test-modem-helpers.c @@ -4278,6 +4278,100 @@ test_ccwa_response (void) } /*****************************************************************************/ +/* 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 */ static void @@ -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)); |