mirror of git://git.sysmocom.de/ofono
Add Cell Broadcast assembly utilities
This commit is contained in:
parent
bf2543b207
commit
a9f776123e
259
src/smsutil.c
259
src/smsutil.c
|
@ -2848,3 +2848,262 @@ char *cbs_decode_text(GSList *cbs_list, char *iso639_lang)
|
|||
g_free(buf);
|
||||
return utf8;
|
||||
}
|
||||
|
||||
static inline gboolean cbs_is_update_newer(unsigned int n, unsigned int o)
|
||||
{
|
||||
unsigned int old_update = o & 0xf;
|
||||
unsigned int new_update = n & 0xf;
|
||||
|
||||
if (new_update == old_update)
|
||||
return FALSE;
|
||||
|
||||
/* Any Update Number eight or less higher (modulo 16) than the last
|
||||
* received Update Number will be considered more recent, and shall be
|
||||
* treated as a new CBS message, provided the mobile has not been
|
||||
* switched off.
|
||||
*/
|
||||
if (new_update <= ((old_update + 8) % 16))
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
struct cbs_assembly *cbs_assembly_new()
|
||||
{
|
||||
return g_new0(struct cbs_assembly, 1);
|
||||
}
|
||||
|
||||
void cbs_assembly_free(struct cbs_assembly *assembly)
|
||||
{
|
||||
GSList *l;
|
||||
|
||||
for (l = assembly->assembly_list; l; l = l->next) {
|
||||
struct cbs_assembly_node *node = l->data;
|
||||
|
||||
g_slist_foreach(node->pages, (GFunc)g_free, 0);
|
||||
g_slist_free(node->pages);
|
||||
g_free(node);
|
||||
}
|
||||
|
||||
g_slist_free(assembly->assembly_list);
|
||||
g_slist_free(assembly->recv_plmn);
|
||||
g_slist_free(assembly->recv_loc);
|
||||
g_slist_free(assembly->recv_cell);
|
||||
|
||||
g_free(assembly);
|
||||
}
|
||||
|
||||
static gint cbs_compare_node_by_gs(gconstpointer a, gconstpointer b)
|
||||
{
|
||||
const struct cbs_assembly_node *node = a;
|
||||
unsigned int gs = GPOINTER_TO_UINT(b);
|
||||
|
||||
if (((node->serial >> 14) & 0x3) == gs)
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static gint cbs_compare_node_by_update(gconstpointer a, gconstpointer b)
|
||||
{
|
||||
const struct cbs_assembly_node *node = a;
|
||||
unsigned int serial = GPOINTER_TO_UINT(b);
|
||||
|
||||
if ((serial & (~0xf)) != (node->serial & (~0xf)))
|
||||
return 1;
|
||||
|
||||
if (cbs_is_update_newer(node->serial, serial))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static gint cbs_compare_recv_by_serial(gconstpointer a, gconstpointer b)
|
||||
{
|
||||
unsigned int old_serial = GPOINTER_TO_UINT(a);
|
||||
unsigned int new_serial = GPOINTER_TO_UINT(b);
|
||||
|
||||
if ((old_serial & (~0xf)) == (new_serial & (~0xf)))
|
||||
return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void cbs_assembly_expire(struct cbs_assembly *assembly,
|
||||
GCompareFunc func, gconstpointer *userdata)
|
||||
{
|
||||
GSList *l;
|
||||
GSList *prev;
|
||||
GSList *tmp;
|
||||
|
||||
/* Take care of the case where several updates are being
|
||||
* reassembled at the same time. If the newer one is assembled
|
||||
* first, then the subsequent old update is discarded, make
|
||||
* sure that we're also discarding the assembly node for the
|
||||
* partially assembled ones
|
||||
*/
|
||||
prev = NULL;
|
||||
l = assembly->assembly_list;
|
||||
|
||||
while (l) {
|
||||
struct cbs_assembly_node *node = l->data;
|
||||
|
||||
if (func(node, userdata) != 0) {
|
||||
prev = l;
|
||||
l = l->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (prev)
|
||||
prev->next = l->next;
|
||||
else
|
||||
assembly->assembly_list = l->next;
|
||||
|
||||
g_slist_foreach(node->pages, (GFunc)g_free, NULL);
|
||||
g_slist_free(node->pages);
|
||||
g_free(node->pages);
|
||||
tmp = l;
|
||||
l = l->next;
|
||||
g_slist_free_1(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
void cbs_assembly_location_changed(struct cbs_assembly *assembly,
|
||||
gboolean lac, gboolean ci)
|
||||
{
|
||||
/* Location Area wide (in GSM) (which means that a CBS message with the
|
||||
* same Message Code and Update Number may or may not be "new" in the
|
||||
* next cell according to whether the next cell is in the same Location
|
||||
* Area as the current cell), or
|
||||
*
|
||||
* Service Area Wide (in UMTS) (which means that a CBS message with the
|
||||
* same Message Code and Update Number may or may not be "new" in the
|
||||
* next cell according to whether the next cell is in the same Service
|
||||
* Area as the current cell)
|
||||
*
|
||||
* NOTE 4: According to 3GPP TS 23.003 [2] a Service Area consists of
|
||||
* one cell only.
|
||||
*/
|
||||
|
||||
if (lac) {
|
||||
/* If LAC changed, then cell id has changed */
|
||||
ci = TRUE;
|
||||
g_slist_free(assembly->recv_loc);
|
||||
assembly->recv_loc = NULL;
|
||||
|
||||
cbs_assembly_expire(assembly, cbs_compare_node_by_gs,
|
||||
GUINT_TO_POINTER(CBS_GEO_SCOPE_SERVICE_AREA));
|
||||
}
|
||||
|
||||
if (ci) {
|
||||
g_slist_free(assembly->recv_cell);
|
||||
assembly->recv_cell = NULL;
|
||||
cbs_assembly_expire(assembly, cbs_compare_node_by_gs,
|
||||
GUINT_TO_POINTER(CBS_GEO_SCOPE_CELL_IMMEDIATE));
|
||||
cbs_assembly_expire(assembly, cbs_compare_node_by_gs,
|
||||
GUINT_TO_POINTER(CBS_GEO_SCOPE_CELL_NORMAL));
|
||||
}
|
||||
}
|
||||
|
||||
GSList *cbs_assembly_add_page(struct cbs_assembly *assembly,
|
||||
const struct cbs *cbs)
|
||||
{
|
||||
struct cbs *newcbs;
|
||||
struct cbs_assembly_node *node;
|
||||
GSList *completed;
|
||||
unsigned int new_serial;
|
||||
GSList **recv;
|
||||
GSList *l;
|
||||
GSList *prev;
|
||||
int position;
|
||||
|
||||
new_serial = cbs->gs << 14;
|
||||
new_serial |= cbs->message_code << 4;
|
||||
new_serial |= cbs->update_number;
|
||||
new_serial |= cbs->message_identifier << 16;
|
||||
|
||||
if (cbs->gs == CBS_GEO_SCOPE_PLMN)
|
||||
recv = &assembly->recv_plmn;
|
||||
else if (cbs->gs == CBS_GEO_SCOPE_SERVICE_AREA)
|
||||
recv = &assembly->recv_loc;
|
||||
else
|
||||
recv = &assembly->recv_cell;
|
||||
|
||||
/* Have we seen this message before? */
|
||||
l = g_slist_find_custom(*recv, GUINT_TO_POINTER(new_serial),
|
||||
cbs_compare_recv_by_serial);
|
||||
|
||||
/* If we have, is the message newer? */
|
||||
if (l && !cbs_is_update_newer(new_serial, GPOINTER_TO_UINT(l->data)))
|
||||
return NULL;
|
||||
|
||||
/* Easy case first, page 1 of 1 */
|
||||
if (cbs->max_pages == 1 && cbs->page == 1) {
|
||||
if (l)
|
||||
l->data = GUINT_TO_POINTER(new_serial);
|
||||
else
|
||||
*recv = g_slist_prepend(*recv,
|
||||
GUINT_TO_POINTER(new_serial));
|
||||
|
||||
newcbs = g_new(struct cbs, 1);
|
||||
memcpy(newcbs, cbs, sizeof(struct cbs));
|
||||
completed = g_slist_append(NULL, newcbs);
|
||||
|
||||
return completed;
|
||||
}
|
||||
|
||||
prev = NULL;
|
||||
position = 0;
|
||||
|
||||
for (l = assembly->assembly_list; l; prev = l, l = l->next) {
|
||||
int j;
|
||||
node = l->data;
|
||||
|
||||
if (new_serial != node->serial)
|
||||
continue;
|
||||
|
||||
if (node->bitmap & (1 << cbs->page))
|
||||
return NULL;
|
||||
|
||||
for (j = 1; j < cbs->page; j++)
|
||||
if (node->bitmap & (1 << j))
|
||||
position += 1;
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
node = g_new0(struct cbs_assembly_node, 1);
|
||||
node->serial = new_serial;
|
||||
|
||||
assembly->assembly_list = g_slist_prepend(assembly->assembly_list,
|
||||
node);
|
||||
|
||||
prev = NULL;
|
||||
l = assembly->assembly_list;
|
||||
position = 0;
|
||||
|
||||
out:
|
||||
newcbs = g_new(struct cbs, 1);
|
||||
memcpy(newcbs, cbs, sizeof(struct cbs));
|
||||
node->pages = g_slist_insert(node->pages, newcbs, position);
|
||||
node->bitmap |= 1 << cbs->page;
|
||||
|
||||
if (g_slist_length(node->pages) < cbs->max_pages)
|
||||
return NULL;
|
||||
|
||||
completed = node->pages;
|
||||
|
||||
if (prev)
|
||||
prev->next = l->next;
|
||||
else
|
||||
assembly->assembly_list = l->next;
|
||||
|
||||
g_free(node);
|
||||
g_slist_free_1(l);
|
||||
|
||||
cbs_assembly_expire(assembly, cbs_compare_node_by_update,
|
||||
GUINT_TO_POINTER(new_serial));
|
||||
*recv = g_slist_prepend(*recv, GUINT_TO_POINTER(new_serial));
|
||||
|
||||
return completed;
|
||||
}
|
||||
|
|
|
@ -373,6 +373,19 @@ struct cbs {
|
|||
guint8 ud[82];
|
||||
};
|
||||
|
||||
struct cbs_assembly_node {
|
||||
guint32 serial;
|
||||
guint16 bitmap;
|
||||
GSList *pages;
|
||||
};
|
||||
|
||||
struct cbs_assembly {
|
||||
GSList *assembly_list;
|
||||
GSList *recv_plmn;
|
||||
GSList *recv_loc;
|
||||
GSList *recv_cell;
|
||||
};
|
||||
|
||||
static inline gboolean is_bit_set(unsigned char oct, int bit)
|
||||
{
|
||||
int mask = 0x1 << bit;
|
||||
|
@ -453,3 +466,10 @@ gboolean cbs_extract_app_port(const struct cbs *cbs, int *dst, int *src,
|
|||
gboolean *is_8bit);
|
||||
|
||||
char *cbs_decode_text(GSList *cbs_list, char *iso639_lang);
|
||||
|
||||
struct cbs_assembly *cbs_assembly_new();
|
||||
void cbs_assembly_free(struct cbs_assembly *assembly);
|
||||
GSList *cbs_assembly_add_page(struct cbs_assembly *assembly,
|
||||
const struct cbs *cbs);
|
||||
void cbs_assembly_location_changed(struct cbs_assembly *assembly,
|
||||
gboolean lac, gboolean ci);
|
||||
|
|
Loading…
Reference in New Issue