cbm: Add CellBroadcast interface
This adds support for the Cell Broadcast interface allowing to receive, list, read and delete Cell Broadcast messages via the org.freedesktop.ModemManager1.Modem.CellBroadcast and org.freedesktop.ModemManager1.Cbm interfaces. Signed-off-by: Guido Günther <agx@sigxcpu.org>
This commit is contained in:
committed by
Aleksander Morgado
parent
eedf78d662
commit
62f7b76e8e
@@ -133,6 +133,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|||||||
/* Prefix for object paths */
|
/* Prefix for object paths */
|
||||||
#define MM_DBUS_MODEM_PREFIX MM_DBUS_PATH "/Modem"
|
#define MM_DBUS_MODEM_PREFIX MM_DBUS_PATH "/Modem"
|
||||||
#define MM_DBUS_BEARER_PREFIX MM_DBUS_PATH "/Bearer"
|
#define MM_DBUS_BEARER_PREFIX MM_DBUS_PATH "/Bearer"
|
||||||
|
#define MM_DBUS_CBM_PREFIX MM_DBUS_PATH "/CBM"
|
||||||
#define MM_DBUS_SIM_PREFIX MM_DBUS_PATH "/SIM"
|
#define MM_DBUS_SIM_PREFIX MM_DBUS_PATH "/SIM"
|
||||||
#define MM_DBUS_SMS_PREFIX MM_DBUS_PATH "/SMS"
|
#define MM_DBUS_SMS_PREFIX MM_DBUS_PATH "/SMS"
|
||||||
#define MM_DBUS_CALL_PREFIX MM_DBUS_PATH "/Call"
|
#define MM_DBUS_CALL_PREFIX MM_DBUS_PATH "/Call"
|
||||||
|
|||||||
@@ -1107,6 +1107,22 @@ typedef enum { /*< underscore_name=mm_sms_cdma_service_category >*/
|
|||||||
MM_SMS_CDMA_SERVICE_CATEGORY_CMAS_TEST = 0x1004,
|
MM_SMS_CDMA_SERVICE_CATEGORY_CMAS_TEST = 0x1004,
|
||||||
} MMSmsCdmaServiceCategory;
|
} MMSmsCdmaServiceCategory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* MMCbmState:
|
||||||
|
* @MM_CBM_STATE_UNKNOWN: State unknown or not reportable.
|
||||||
|
* @MM_CBM_STATE_RECEIVING: The message is being received but is not yet complete.
|
||||||
|
* @MM_CBM_STATE_RECEIVED: The message has been completely received.
|
||||||
|
*
|
||||||
|
* State of a given CBM.
|
||||||
|
*
|
||||||
|
* Since: 1.24
|
||||||
|
*/
|
||||||
|
typedef enum { /*< underscore_name=mm_cbm_state >*/
|
||||||
|
MM_CBM_STATE_UNKNOWN = 0,
|
||||||
|
MM_CBM_STATE_RECEIVING = 1,
|
||||||
|
MM_CBM_STATE_RECEIVED = 2,
|
||||||
|
} MMCbmState;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* MMModemLocationSource:
|
* MMModemLocationSource:
|
||||||
* @MM_MODEM_LOCATION_SOURCE_NONE: None.
|
* @MM_MODEM_LOCATION_SOURCE_NONE: None.
|
||||||
|
|||||||
@@ -7,7 +7,9 @@
|
|||||||
<xi:include href="org.freedesktop.ModemManager1.Bearer.xml"/>
|
<xi:include href="org.freedesktop.ModemManager1.Bearer.xml"/>
|
||||||
<xi:include href="org.freedesktop.ModemManager1.Sms.xml"/>
|
<xi:include href="org.freedesktop.ModemManager1.Sms.xml"/>
|
||||||
<xi:include href="org.freedesktop.ModemManager1.Call.xml"/>
|
<xi:include href="org.freedesktop.ModemManager1.Call.xml"/>
|
||||||
|
<xi:include href="org.freedesktop.ModemManager1.Cbm.xml"/>
|
||||||
<xi:include href="org.freedesktop.ModemManager1.Modem.xml"/>
|
<xi:include href="org.freedesktop.ModemManager1.Modem.xml"/>
|
||||||
|
<xi:include href="org.freedesktop.ModemManager1.Modem.CellBroadcast.xml"/>
|
||||||
<xi:include href="org.freedesktop.ModemManager1.Modem.Voice.xml"/>
|
<xi:include href="org.freedesktop.ModemManager1.Modem.Voice.xml"/>
|
||||||
<xi:include href="org.freedesktop.ModemManager1.Modem.Modem3gpp.xml"/>
|
<xi:include href="org.freedesktop.ModemManager1.Modem.Modem3gpp.xml"/>
|
||||||
<xi:include href="org.freedesktop.ModemManager1.Modem.Modem3gpp.ProfileManager.xml"/>
|
<xi:include href="org.freedesktop.ModemManager1.Modem.Modem3gpp.ProfileManager.xml"/>
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ mm_ifaces_bearer = files('org.freedesktop.ModemManager1.Bearer.xml')
|
|||||||
mm_ifaces_call = files('org.freedesktop.ModemManager1.Call.xml')
|
mm_ifaces_call = files('org.freedesktop.ModemManager1.Call.xml')
|
||||||
|
|
||||||
mm_ifaces_modem = files(
|
mm_ifaces_modem = files(
|
||||||
|
'org.freedesktop.ModemManager1.Modem.CellBroadcast.xml',
|
||||||
'org.freedesktop.ModemManager1.Modem.Firmware.xml',
|
'org.freedesktop.ModemManager1.Modem.Firmware.xml',
|
||||||
'org.freedesktop.ModemManager1.Modem.Location.xml',
|
'org.freedesktop.ModemManager1.Modem.Location.xml',
|
||||||
'org.freedesktop.ModemManager1.Modem.Messaging.xml',
|
'org.freedesktop.ModemManager1.Modem.Messaging.xml',
|
||||||
@@ -29,10 +30,12 @@ mm_ifaces_modem = files(
|
|||||||
'org.freedesktop.ModemManager1.Modem.xml',
|
'org.freedesktop.ModemManager1.Modem.xml',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
mm_ifaces_cbm = files('org.freedesktop.ModemManager1.Cbm.xml',)
|
||||||
mm_ifaces_sim = files('org.freedesktop.ModemManager1.Sim.xml')
|
mm_ifaces_sim = files('org.freedesktop.ModemManager1.Sim.xml')
|
||||||
mm_ifaces_sms = files('org.freedesktop.ModemManager1.Sms.xml',)
|
mm_ifaces_sms = files('org.freedesktop.ModemManager1.Sms.xml',)
|
||||||
|
|
||||||
install_data(
|
install_data(
|
||||||
mm_ifaces + mm_ifaces_bearer + mm_ifaces_call + mm_ifaces_modem + mm_ifaces_sim + mm_ifaces_sms,
|
mm_ifaces + mm_ifaces_bearer + mm_ifaces_call + mm_ifaces_cbm +
|
||||||
|
mm_ifaces_modem + mm_ifaces_sim + mm_ifaces_sms,
|
||||||
install_dir: dbus_interfaces_dir,
|
install_dir: dbus_interfaces_dir,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,69 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
ModemManager 1.0 Interface Specification
|
||||||
|
|
||||||
|
Copyright (C) 2024 Guido Günther <agx@sigxcpu.org>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
org.freedesktop.ModemManager1.Cbm:
|
||||||
|
@short_description: The ModemManager Cbm interface.
|
||||||
|
|
||||||
|
The Cbm interface defines operations and properties of a single cell broadcast message.
|
||||||
|
-->
|
||||||
|
<interface name="org.freedesktop.ModemManager1.Cbm">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
State:
|
||||||
|
|
||||||
|
A <link linkend="MMCbmState">MMCbmState</link> value,
|
||||||
|
describing the state of the message.
|
||||||
|
|
||||||
|
Since: 1.24
|
||||||
|
-->
|
||||||
|
<property name="State" type="u" access="read" />
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Text:
|
||||||
|
|
||||||
|
Message text, in UTF-8.
|
||||||
|
|
||||||
|
Since: 1.24
|
||||||
|
-->
|
||||||
|
<property name="Text" type="s" access="read" />
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Channel:
|
||||||
|
|
||||||
|
The channel (or message identifier) specifying the source and
|
||||||
|
type of the cell broadcast message.
|
||||||
|
|
||||||
|
Since: 1.24
|
||||||
|
-->
|
||||||
|
<property name="Channel" type="u" access="read" />
|
||||||
|
|
||||||
|
<!--
|
||||||
|
MessageCode:
|
||||||
|
|
||||||
|
The message code of this CBM. The message code differentiates
|
||||||
|
between CBMs from the same channel.
|
||||||
|
|
||||||
|
Since: 1.24
|
||||||
|
-->
|
||||||
|
<property name="MessageCode" type="u" access="read" />
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Update:
|
||||||
|
|
||||||
|
The update number of this CBM. The update number allows to update CBMs
|
||||||
|
with the same channel and message code.
|
||||||
|
|
||||||
|
Since: 1.24
|
||||||
|
-->
|
||||||
|
<property name="Update" type="u" access="read" />
|
||||||
|
|
||||||
|
</interface>
|
||||||
|
</node>
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
ModemManager 1.0 Interface Specification
|
||||||
|
|
||||||
|
Copyright (C) 2024 Guido Günther <agx@sigxcpu.org>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
|
||||||
|
|
||||||
|
<!--
|
||||||
|
org.freedesktop.ModemManager1.Modem.CellBroadcast:
|
||||||
|
@short_description: The ModemManager CellBroadcast service interface.
|
||||||
|
|
||||||
|
This interface allows clients to configure the details of CellBroadcast
|
||||||
|
service information.
|
||||||
|
|
||||||
|
This interface will only be available once the modem is ready to be
|
||||||
|
registered in the cellular network.
|
||||||
|
-->
|
||||||
|
<interface name="org.freedesktop.ModemManager1.Modem.CellBroadcast">
|
||||||
|
<!--
|
||||||
|
List:
|
||||||
|
@result: The list of CBM object paths.
|
||||||
|
|
||||||
|
Retrieve all cell broadcast messages.
|
||||||
|
|
||||||
|
This method should only be used once and subsequent information
|
||||||
|
retrieved either by listening for the
|
||||||
|
#org.freedesktop.ModemManager1.Modem.CellBroadcast::Added signal,
|
||||||
|
or by querying the specific CBM object of interest.
|
||||||
|
|
||||||
|
Since: 1.24
|
||||||
|
-->
|
||||||
|
<method name="List">
|
||||||
|
<arg name="result" type="ao" direction="out" />
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Delete:
|
||||||
|
@path: The object path of the CBM to delete.
|
||||||
|
|
||||||
|
Delete an cell broadcast message
|
||||||
|
|
||||||
|
Since: 1.24
|
||||||
|
-->
|
||||||
|
<method name="Delete">
|
||||||
|
<arg name="path" type="o" direction="in" />
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Added:
|
||||||
|
@path: Object path of the new CBM.
|
||||||
|
|
||||||
|
Emitted when any part of a new cell broadcast message has been
|
||||||
|
received. Not all parts may have been received and the message may not
|
||||||
|
be complete.
|
||||||
|
|
||||||
|
Check the
|
||||||
|
'<link linkend="gdbus-property-org-freedesktop-ModemManager1-Cbm.State">State</link>'
|
||||||
|
property to determine if the message is complete.
|
||||||
|
|
||||||
|
Since: 1.24
|
||||||
|
-->
|
||||||
|
<signal name="Added">
|
||||||
|
<arg name="path" type="o" />
|
||||||
|
</signal>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Deleted:
|
||||||
|
@path: Object path of the now deleted CBM.
|
||||||
|
|
||||||
|
Emitted when a message has been deleted.
|
||||||
|
|
||||||
|
Since: 1.24
|
||||||
|
-->
|
||||||
|
<signal name="Deleted">
|
||||||
|
<arg name="path" type="o" />
|
||||||
|
</signal>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
CellBroadcasts:
|
||||||
|
|
||||||
|
The list of CBM object paths.
|
||||||
|
|
||||||
|
Since: 1.24
|
||||||
|
-->
|
||||||
|
<property name="CellBroadcasts" type="ao" access="read" />
|
||||||
|
|
||||||
|
</interface>
|
||||||
|
</node>
|
||||||
@@ -132,6 +132,7 @@ gen_sources += custom_target(
|
|||||||
gdbus_ifaces = {
|
gdbus_ifaces = {
|
||||||
'bearer': {'sources': mm_ifaces_bearer, 'object_manager': false},
|
'bearer': {'sources': mm_ifaces_bearer, 'object_manager': false},
|
||||||
'call': {'sources': mm_ifaces_call, 'object_manager': false},
|
'call': {'sources': mm_ifaces_call, 'object_manager': false},
|
||||||
|
'cbm': {'sources': mm_ifaces_cbm, 'object_manager': false},
|
||||||
'manager': {'sources': mm_ifaces, 'object_manager': false},
|
'manager': {'sources': mm_ifaces, 'object_manager': false},
|
||||||
'sim': {'sources': mm_ifaces_sim, 'object_manager': false},
|
'sim': {'sources': mm_ifaces_sim, 'object_manager': false},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -103,6 +103,7 @@
|
|||||||
#include <mm-gdbus-manager.h>
|
#include <mm-gdbus-manager.h>
|
||||||
#include <mm-gdbus-modem.h>
|
#include <mm-gdbus-modem.h>
|
||||||
#include <mm-gdbus-bearer.h>
|
#include <mm-gdbus-bearer.h>
|
||||||
|
#include <mm-gdbus-cbm.h>
|
||||||
#include <mm-gdbus-sim.h>
|
#include <mm-gdbus-sim.h>
|
||||||
#include <mm-gdbus-sms.h>
|
#include <mm-gdbus-sms.h>
|
||||||
#include <mm-gdbus-call.h>
|
#include <mm-gdbus-call.h>
|
||||||
|
|||||||
@@ -259,6 +259,7 @@ sources = files(
|
|||||||
'mm-auth-provider.c',
|
'mm-auth-provider.c',
|
||||||
'mm-base-bearer.c',
|
'mm-base-bearer.c',
|
||||||
'mm-base-call.c',
|
'mm-base-call.c',
|
||||||
|
'mm-base-cbm.c',
|
||||||
'mm-base-manager.c',
|
'mm-base-manager.c',
|
||||||
'mm-base-modem-at.c',
|
'mm-base-modem-at.c',
|
||||||
'mm-base-modem.c',
|
'mm-base-modem.c',
|
||||||
@@ -268,6 +269,7 @@ sources = files(
|
|||||||
'mm-broadband-bearer.c',
|
'mm-broadband-bearer.c',
|
||||||
'mm-broadband-modem.c',
|
'mm-broadband-modem.c',
|
||||||
'mm-call-list.c',
|
'mm-call-list.c',
|
||||||
|
'mm-cbm-list.c',
|
||||||
'mm-context.c',
|
'mm-context.c',
|
||||||
'mm-device.c',
|
'mm-device.c',
|
||||||
'mm-dispatcher.c',
|
'mm-dispatcher.c',
|
||||||
@@ -280,6 +282,7 @@ sources = files(
|
|||||||
'mm-iface-modem-3gpp-ussd.c',
|
'mm-iface-modem-3gpp-ussd.c',
|
||||||
'mm-iface-modem.c',
|
'mm-iface-modem.c',
|
||||||
'mm-iface-modem-cdma.c',
|
'mm-iface-modem-cdma.c',
|
||||||
|
'mm-iface-modem-cell-broadcast.c',
|
||||||
'mm-iface-modem-firmware.c',
|
'mm-iface-modem-firmware.c',
|
||||||
'mm-iface-modem-location.c',
|
'mm-iface-modem-location.c',
|
||||||
'mm-iface-modem-messaging.c',
|
'mm-iface-modem-messaging.c',
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
#define MM_AUTHORIZATION_LOCATION "org.freedesktop.ModemManager1.Location"
|
#define MM_AUTHORIZATION_LOCATION "org.freedesktop.ModemManager1.Location"
|
||||||
#define MM_AUTHORIZATION_TIME "org.freedesktop.ModemManager1.Time"
|
#define MM_AUTHORIZATION_TIME "org.freedesktop.ModemManager1.Time"
|
||||||
#define MM_AUTHORIZATION_FIRMWARE "org.freedesktop.ModemManager1.Firmware"
|
#define MM_AUTHORIZATION_FIRMWARE "org.freedesktop.ModemManager1.Firmware"
|
||||||
|
#define MM_AUTHORIZATION_CELL_BROADCAST "org.freedesktop.ModemManager1.CellBroadcast"
|
||||||
|
|
||||||
typedef struct _MMAuthProvider MMAuthProvider;
|
typedef struct _MMAuthProvider MMAuthProvider;
|
||||||
typedef struct _MMAuthProviderClass MMAuthProviderClass;
|
typedef struct _MMAuthProviderClass MMAuthProviderClass;
|
||||||
|
|||||||
@@ -0,0 +1,571 @@
|
|||||||
|
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||||
|
/*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details:
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 Guido Günther <agx@sigxcpu.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include <ModemManager.h>
|
||||||
|
#define _LIBMM_INSIDE_MM
|
||||||
|
#include <libmm-glib.h>
|
||||||
|
|
||||||
|
#include "mm-base-cbm.h"
|
||||||
|
#include "mm-broadband-modem.h"
|
||||||
|
#include "mm-iface-modem.h"
|
||||||
|
#include "mm-iface-modem-cell-broadcast.h"
|
||||||
|
#include "mm-cbm-part.h"
|
||||||
|
#include "mm-base-modem-at.h"
|
||||||
|
#include "mm-base-modem.h"
|
||||||
|
#include "mm-log-object.h"
|
||||||
|
#include "mm-modem-helpers.h"
|
||||||
|
#include "mm-error-helpers.h"
|
||||||
|
|
||||||
|
static void log_object_iface_init (MMLogObjectInterface *iface);
|
||||||
|
|
||||||
|
G_DEFINE_TYPE_EXTENDED (MMBaseCbm, mm_base_cbm, MM_GDBUS_TYPE_CBM_SKELETON, 0,
|
||||||
|
G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init))
|
||||||
|
|
||||||
|
enum {
|
||||||
|
PROP_0,
|
||||||
|
PROP_PATH,
|
||||||
|
PROP_CONNECTION,
|
||||||
|
PROP_MODEM,
|
||||||
|
PROP_MAX_PARTS,
|
||||||
|
PROP_SERIAL,
|
||||||
|
PROP_LAST
|
||||||
|
};
|
||||||
|
|
||||||
|
static GParamSpec *properties[PROP_LAST];
|
||||||
|
|
||||||
|
struct _MMBaseCbmPrivate {
|
||||||
|
/* The connection to the system bus */
|
||||||
|
GDBusConnection *connection;
|
||||||
|
guint dbus_id;
|
||||||
|
|
||||||
|
/* The modem which owns this CBM */
|
||||||
|
MMBaseModem *modem;
|
||||||
|
/* The path where the CBM object is exported */
|
||||||
|
gchar *path;
|
||||||
|
|
||||||
|
/* List of CBM parts */
|
||||||
|
guint max_parts;
|
||||||
|
GList *parts;
|
||||||
|
|
||||||
|
/* channel and serial identify the CBM */
|
||||||
|
guint16 serial;
|
||||||
|
|
||||||
|
/* Set to true when all needed parts were received,
|
||||||
|
* parsed and assembled */
|
||||||
|
gboolean is_assembled;
|
||||||
|
};
|
||||||
|
|
||||||
|
void
|
||||||
|
mm_base_cbm_export (MMBaseCbm *self)
|
||||||
|
{
|
||||||
|
g_autofree gchar *path = NULL;
|
||||||
|
|
||||||
|
path = g_strdup_printf (MM_DBUS_CBM_PREFIX "/%d", self->priv->dbus_id);
|
||||||
|
g_object_set (self,
|
||||||
|
MM_BASE_CBM_PATH, path,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
mm_base_cbm_unexport (MMBaseCbm *self)
|
||||||
|
{
|
||||||
|
g_object_set (self,
|
||||||
|
MM_BASE_CBM_PATH, NULL,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static void
|
||||||
|
cbm_dbus_export (MMBaseCbm *self)
|
||||||
|
{
|
||||||
|
g_autoptr (GError) error = NULL;
|
||||||
|
|
||||||
|
if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (self),
|
||||||
|
self->priv->connection,
|
||||||
|
self->priv->path,
|
||||||
|
&error)) {
|
||||||
|
mm_obj_warn (self, "couldn't export CBM: %s", error->message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
cbm_dbus_unexport (MMBaseCbm *self)
|
||||||
|
{
|
||||||
|
/* Only unexport if currently exported */
|
||||||
|
if (g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (self)))
|
||||||
|
g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (self));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
const gchar *
|
||||||
|
mm_base_cbm_get_path (MMBaseCbm *self)
|
||||||
|
{
|
||||||
|
return self->priv->path;
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
mm_base_cbm_is_complete (MMBaseCbm *self)
|
||||||
|
{
|
||||||
|
return (g_list_length (self->priv->parts) == self->priv->max_parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static guint
|
||||||
|
cmp_cbm_part_has_num (MMCbmPart *part,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
return (GPOINTER_TO_UINT (user_data) - mm_cbm_part_get_part_num (part));
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
mm_base_cbm_has_part_num (MMBaseCbm *self,
|
||||||
|
guint part_num)
|
||||||
|
{
|
||||||
|
return !!g_list_find_custom (self->priv->parts,
|
||||||
|
GUINT_TO_POINTER (part_num),
|
||||||
|
(GCompareFunc)cmp_cbm_part_has_num);
|
||||||
|
}
|
||||||
|
|
||||||
|
GList *
|
||||||
|
mm_base_cbm_get_parts (MMBaseCbm *self)
|
||||||
|
{
|
||||||
|
return self->priv->parts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static void
|
||||||
|
initialize_cbm (MMBaseCbm *self)
|
||||||
|
{
|
||||||
|
MMCbmPart *part;
|
||||||
|
guint16 serial;
|
||||||
|
|
||||||
|
/* Some of the fields of the CBM object may be initialized as soon as we have
|
||||||
|
* one part already available, even if it's not exactly the first one */
|
||||||
|
g_assert (self->priv->parts);
|
||||||
|
part = (MMCbmPart *)(self->priv->parts->data);
|
||||||
|
|
||||||
|
serial = mm_cbm_part_get_serial (part);
|
||||||
|
g_object_set (self,
|
||||||
|
MM_BASE_CBM_MAX_PARTS, mm_cbm_part_get_num_parts (part),
|
||||||
|
MM_BASE_CBM_SERIAL, serial,
|
||||||
|
"channel", mm_cbm_part_get_channel (part),
|
||||||
|
"update", CBM_SERIAL_MESSAGE_CODE_UPDATE (serial),
|
||||||
|
"message-code", CBM_SERIAL_MESSAGE_CODE (serial),
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
assemble_cbm (MMBaseCbm *self,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
GList *l;
|
||||||
|
guint part_num, idx;
|
||||||
|
g_autofree MMCbmPart **sorted_parts = NULL;
|
||||||
|
g_autoptr (GString) fulltext = NULL;
|
||||||
|
|
||||||
|
sorted_parts = g_new0 (MMCbmPart *, self->priv->max_parts);
|
||||||
|
|
||||||
|
/* Check if we have duplicate parts */
|
||||||
|
for (l = self->priv->parts; l; l = g_list_next (l)) {
|
||||||
|
part_num = mm_cbm_part_get_part_num ((MMCbmPart *)l->data);
|
||||||
|
|
||||||
|
/* part_num starts at 1, array starts at 0 */
|
||||||
|
idx = part_num - 1;
|
||||||
|
if (part_num < 1 || part_num > self->priv->max_parts) {
|
||||||
|
mm_obj_warn (self, "invalid part part nu (%u) found, ignoring", part_num);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sorted_parts[idx]) {
|
||||||
|
mm_obj_warn (self, "duplicate part index (%u) found, ignoring", part_num);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Add the part to the proper array position */
|
||||||
|
sorted_parts[idx] = (MMCbmPart *)l->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
fulltext = g_string_new ("");
|
||||||
|
|
||||||
|
/* Assemble text and data from all parts. */
|
||||||
|
for (idx = 0; idx < self->priv->max_parts; idx++) {
|
||||||
|
const gchar *parttext;
|
||||||
|
|
||||||
|
if (!sorted_parts[idx]) {
|
||||||
|
g_set_error (error,
|
||||||
|
MM_CORE_ERROR,
|
||||||
|
MM_CORE_ERROR_FAILED,
|
||||||
|
"Cannot assemble CBM, missing part at index (%u)",
|
||||||
|
idx);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
parttext = mm_cbm_part_get_text (sorted_parts[idx]);
|
||||||
|
if (!parttext) {
|
||||||
|
g_set_error (error,
|
||||||
|
MM_CORE_ERROR,
|
||||||
|
MM_CORE_ERROR_FAILED,
|
||||||
|
"Cannot assemble CBM, part at index (%u) has no text",
|
||||||
|
idx);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parttext)
|
||||||
|
g_string_append (fulltext, parttext);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If we got all parts, we also have the first one always */
|
||||||
|
g_assert (sorted_parts[0] != NULL);
|
||||||
|
|
||||||
|
/* If we got everything, assemble the text! */
|
||||||
|
g_object_set (self,
|
||||||
|
"text", fulltext->str,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
self->priv->is_assembled = TRUE;
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static guint
|
||||||
|
cmp_cbm_part_num (MMCbmPart *a,
|
||||||
|
MMCbmPart *b)
|
||||||
|
{
|
||||||
|
return (mm_cbm_part_get_part_num (a) - mm_cbm_part_get_part_num (b));
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
mm_base_cbm_take_part (MMBaseCbm *self,
|
||||||
|
MMCbmPart *part,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
if (g_list_length (self->priv->parts) >= self->priv->max_parts) {
|
||||||
|
g_set_error (error,
|
||||||
|
MM_CORE_ERROR,
|
||||||
|
MM_CORE_ERROR_FAILED,
|
||||||
|
"Already took %u parts, cannot take more",
|
||||||
|
g_list_length (self->priv->parts));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_list_find_custom (self->priv->parts,
|
||||||
|
part,
|
||||||
|
(GCompareFunc)cmp_cbm_part_num)) {
|
||||||
|
g_set_error (error,
|
||||||
|
MM_CORE_ERROR,
|
||||||
|
MM_CORE_ERROR_FAILED,
|
||||||
|
"Cannot take part, part %u already taken",
|
||||||
|
mm_cbm_part_get_part_num (part));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mm_cbm_part_get_part_num (part) > self->priv->max_parts) {
|
||||||
|
g_set_error (error,
|
||||||
|
MM_CORE_ERROR,
|
||||||
|
MM_CORE_ERROR_FAILED,
|
||||||
|
"Cannot take part with number %u, maximum is %u",
|
||||||
|
mm_cbm_part_get_part_num (part),
|
||||||
|
self->priv->max_parts);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Insert sorted by concat sequence */
|
||||||
|
self->priv->parts = g_list_insert_sorted (self->priv->parts,
|
||||||
|
part,
|
||||||
|
(GCompareFunc)cmp_cbm_part_num);
|
||||||
|
|
||||||
|
/* If this is the first part we take, initialize common CBM fields */
|
||||||
|
if (g_list_length (self->priv->parts) == 1)
|
||||||
|
initialize_cbm (self);
|
||||||
|
|
||||||
|
/* We only populate contents when the multipart CBM is complete */
|
||||||
|
if (mm_base_cbm_is_complete (self)) {
|
||||||
|
g_autoptr (GError) inner_error = NULL;
|
||||||
|
|
||||||
|
if (!assemble_cbm (self, &inner_error)) {
|
||||||
|
/* We DO NOT propagate the error. The part was properly taken
|
||||||
|
* so ownership passed to the MMBaseCbm object. */
|
||||||
|
mm_obj_warn (self, "couldn't assemble CBM: %s", inner_error->message);
|
||||||
|
} else {
|
||||||
|
/* Completed AND assembled
|
||||||
|
* Change state RECEIVING->RECEIVED, and signal completeness */
|
||||||
|
if (mm_gdbus_cbm_get_state (MM_GDBUS_CBM (self)) == MM_CBM_STATE_RECEIVING)
|
||||||
|
mm_gdbus_cbm_set_state (MM_GDBUS_CBM (self), MM_CBM_STATE_RECEIVED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
MMBaseCbm *
|
||||||
|
mm_base_cbm_new (MMBaseModem *modem)
|
||||||
|
{
|
||||||
|
return MM_BASE_CBM (g_object_new (MM_TYPE_BASE_CBM,
|
||||||
|
MM_BASE_CBM_MODEM, modem,
|
||||||
|
NULL));
|
||||||
|
}
|
||||||
|
|
||||||
|
MMBaseCbm *
|
||||||
|
mm_base_cbm_new_with_part (MMBaseModem *modem,
|
||||||
|
MMCbmState state,
|
||||||
|
guint max_parts,
|
||||||
|
MMCbmPart *first_part,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
MMBaseCbm *self;
|
||||||
|
|
||||||
|
g_assert (MM_IS_IFACE_MODEM_CELL_BROADCAST (modem));
|
||||||
|
|
||||||
|
if (state == MM_CBM_STATE_RECEIVED)
|
||||||
|
state = MM_CBM_STATE_RECEIVING;
|
||||||
|
|
||||||
|
/* Create a CBM object as defined by the interface */
|
||||||
|
self = mm_iface_modem_cell_broadcast_create_cbm (MM_IFACE_MODEM_CELL_BROADCAST (modem));
|
||||||
|
g_object_set (self,
|
||||||
|
MM_BASE_CBM_MAX_PARTS, max_parts,
|
||||||
|
"state", state,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
if (!mm_base_cbm_take_part (self, first_part, error))
|
||||||
|
g_clear_object (&self);
|
||||||
|
|
||||||
|
/* We do export uncomplete multipart messages, so clients can make use of it
|
||||||
|
* Only the STATE of the CBM object will be valid in the exported DBus
|
||||||
|
* interface intially.*/
|
||||||
|
if (self)
|
||||||
|
mm_base_cbm_export (self);
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static gchar *
|
||||||
|
log_object_build_id (MMLogObject *_self)
|
||||||
|
{
|
||||||
|
MMBaseCbm *self;
|
||||||
|
|
||||||
|
self = MM_BASE_CBM (_self);
|
||||||
|
return g_strdup_printf ("cbm%u", self->priv->dbus_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static void
|
||||||
|
set_property (GObject *object,
|
||||||
|
guint prop_id,
|
||||||
|
const GValue *value,
|
||||||
|
GParamSpec *pspec)
|
||||||
|
{
|
||||||
|
MMBaseCbm *self = MM_BASE_CBM (object);
|
||||||
|
|
||||||
|
switch (prop_id) {
|
||||||
|
case PROP_PATH:
|
||||||
|
g_free (self->priv->path);
|
||||||
|
self->priv->path = g_value_dup_string (value);
|
||||||
|
|
||||||
|
/* Export when we get a DBus connection AND we have a path */
|
||||||
|
if (!self->priv->path)
|
||||||
|
cbm_dbus_unexport (self);
|
||||||
|
else if (self->priv->connection)
|
||||||
|
cbm_dbus_export (self);
|
||||||
|
break;
|
||||||
|
case PROP_CONNECTION:
|
||||||
|
g_clear_object (&self->priv->connection);
|
||||||
|
self->priv->connection = g_value_dup_object (value);
|
||||||
|
|
||||||
|
/* Export when we get a DBus connection AND we have a path */
|
||||||
|
if (!self->priv->connection)
|
||||||
|
cbm_dbus_unexport (self);
|
||||||
|
else if (self->priv->path)
|
||||||
|
cbm_dbus_export (self);
|
||||||
|
break;
|
||||||
|
case PROP_MODEM:
|
||||||
|
g_clear_object (&self->priv->modem);
|
||||||
|
self->priv->modem = g_value_dup_object (value);
|
||||||
|
if (self->priv->modem) {
|
||||||
|
/* Set owner ID */
|
||||||
|
mm_log_object_set_owner_id (MM_LOG_OBJECT (self), mm_log_object_get_id (MM_LOG_OBJECT (self->priv->modem)));
|
||||||
|
/* Bind the modem's connection (which is set when it is exported,
|
||||||
|
* and unset when unexported) to the CBM's connection */
|
||||||
|
g_object_bind_property (self->priv->modem, MM_BASE_MODEM_CONNECTION,
|
||||||
|
self, MM_BASE_CBM_CONNECTION,
|
||||||
|
G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case PROP_MAX_PARTS:
|
||||||
|
self->priv->max_parts = g_value_get_uint (value);
|
||||||
|
break;
|
||||||
|
case PROP_SERIAL:
|
||||||
|
self->priv->serial = g_value_get_uint (value);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
get_property (GObject *object,
|
||||||
|
guint prop_id,
|
||||||
|
GValue *value,
|
||||||
|
GParamSpec *pspec)
|
||||||
|
{
|
||||||
|
MMBaseCbm *self = MM_BASE_CBM (object);
|
||||||
|
|
||||||
|
switch (prop_id) {
|
||||||
|
case PROP_PATH:
|
||||||
|
g_value_set_string (value, self->priv->path);
|
||||||
|
break;
|
||||||
|
case PROP_CONNECTION:
|
||||||
|
g_value_set_object (value, self->priv->connection);
|
||||||
|
break;
|
||||||
|
case PROP_MODEM:
|
||||||
|
g_value_set_object (value, self->priv->modem);
|
||||||
|
break;
|
||||||
|
case PROP_MAX_PARTS:
|
||||||
|
g_value_set_uint (value, self->priv->max_parts);
|
||||||
|
break;
|
||||||
|
case PROP_SERIAL:
|
||||||
|
g_value_set_uint (value, self->priv->serial);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
mm_base_cbm_init (MMBaseCbm *self)
|
||||||
|
{
|
||||||
|
static guint id = 0;
|
||||||
|
|
||||||
|
/* Initialize private data */
|
||||||
|
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_BASE_CBM, MMBaseCbmPrivate);
|
||||||
|
self->priv->max_parts = 1;
|
||||||
|
|
||||||
|
/* Each CBM is given a unique id to build its own DBus path */
|
||||||
|
self->priv->dbus_id = id++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
finalize (GObject *object)
|
||||||
|
{
|
||||||
|
MMBaseCbm *self = MM_BASE_CBM (object);
|
||||||
|
|
||||||
|
g_list_free_full (self->priv->parts, (GDestroyNotify)mm_cbm_part_free);
|
||||||
|
g_free (self->priv->path);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (mm_base_cbm_parent_class)->finalize (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
dispose (GObject *object)
|
||||||
|
{
|
||||||
|
MMBaseCbm *self = MM_BASE_CBM (object);
|
||||||
|
|
||||||
|
if (self->priv->connection) {
|
||||||
|
/* If we arrived here with a valid connection, make sure we unexport
|
||||||
|
* the object */
|
||||||
|
cbm_dbus_unexport (self);
|
||||||
|
g_clear_object (&self->priv->connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_clear_object (&self->priv->modem);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (mm_base_cbm_parent_class)->dispose (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
log_object_iface_init (MMLogObjectInterface *iface)
|
||||||
|
{
|
||||||
|
iface->build_id = log_object_build_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
mm_base_cbm_class_init (MMBaseCbmClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
|
||||||
|
g_type_class_add_private (object_class, sizeof (MMBaseCbmPrivate));
|
||||||
|
|
||||||
|
/* Virtual methods */
|
||||||
|
object_class->get_property = get_property;
|
||||||
|
object_class->set_property = set_property;
|
||||||
|
object_class->finalize = finalize;
|
||||||
|
object_class->dispose = dispose;
|
||||||
|
|
||||||
|
properties[PROP_CONNECTION] =
|
||||||
|
g_param_spec_object (MM_BASE_CBM_CONNECTION,
|
||||||
|
"Connection",
|
||||||
|
"GDBus connection to the system bus.",
|
||||||
|
G_TYPE_DBUS_CONNECTION,
|
||||||
|
G_PARAM_READWRITE);
|
||||||
|
|
||||||
|
properties[PROP_PATH] =
|
||||||
|
g_param_spec_string (MM_BASE_CBM_PATH,
|
||||||
|
"Path",
|
||||||
|
"DBus path of the CBM",
|
||||||
|
NULL,
|
||||||
|
G_PARAM_READWRITE);
|
||||||
|
|
||||||
|
properties[PROP_MODEM] =
|
||||||
|
g_param_spec_object (MM_BASE_CBM_MODEM,
|
||||||
|
"Modem",
|
||||||
|
"The Modem which owns this CBM",
|
||||||
|
MM_TYPE_BASE_MODEM,
|
||||||
|
G_PARAM_READWRITE);
|
||||||
|
|
||||||
|
properties[PROP_MAX_PARTS] =
|
||||||
|
g_param_spec_uint (MM_BASE_CBM_MAX_PARTS,
|
||||||
|
"Max parts",
|
||||||
|
"Maximum number of parts composing this CBM",
|
||||||
|
1, 255, 1,
|
||||||
|
G_PARAM_READWRITE);
|
||||||
|
|
||||||
|
properties[PROP_SERIAL] =
|
||||||
|
g_param_spec_uint (MM_BASE_CBM_SERIAL,
|
||||||
|
"Serial",
|
||||||
|
"The serial of this CBM",
|
||||||
|
0, G_MAXUINT16, 0,
|
||||||
|
G_PARAM_READWRITE);
|
||||||
|
|
||||||
|
g_object_class_install_properties (object_class, PROP_LAST, properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
guint16
|
||||||
|
mm_base_cbm_get_serial (MMBaseCbm *self)
|
||||||
|
{
|
||||||
|
return self->priv->serial;
|
||||||
|
}
|
||||||
|
|
||||||
|
guint16
|
||||||
|
mm_base_cbm_get_channel (MMBaseCbm *self)
|
||||||
|
{
|
||||||
|
return mm_gdbus_cbm_get_channel (MM_GDBUS_CBM (self));
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||||
|
/*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details:
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 Guido Günther <agx@sigxcpu.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MM_BASE_CBM_H
|
||||||
|
#define MM_BASE_CBM_H
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
#include <glib-object.h>
|
||||||
|
|
||||||
|
#define _LIBMM_INSIDE_MM
|
||||||
|
#include <libmm-glib.h>
|
||||||
|
|
||||||
|
#include "mm-cbm-part.h"
|
||||||
|
#include "mm-base-modem.h"
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
#define MM_TYPE_BASE_CBM (mm_base_cbm_get_type ())
|
||||||
|
#define MM_BASE_CBM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_BASE_CBM, MMBaseCbm))
|
||||||
|
#define MM_BASE_CBM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_BASE_CBM, MMBaseCbmClass))
|
||||||
|
#define MM_IS_BASE_CBM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_BASE_CBM))
|
||||||
|
#define MM_IS_BASE_CBM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_BASE_CBM))
|
||||||
|
#define MM_BASE_CBM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_BASE_CBM, MMBaseCbmClass))
|
||||||
|
|
||||||
|
typedef struct _MMBaseCbm MMBaseCbm;
|
||||||
|
typedef struct _MMBaseCbmClass MMBaseCbmClass;
|
||||||
|
typedef struct _MMBaseCbmPrivate MMBaseCbmPrivate;
|
||||||
|
|
||||||
|
#define MM_BASE_CBM_PATH "cbm-path"
|
||||||
|
#define MM_BASE_CBM_CONNECTION "cbm-connection"
|
||||||
|
#define MM_BASE_CBM_MODEM "cbm-modem"
|
||||||
|
#define MM_BASE_CBM_MAX_PARTS "cbm-max-parts"
|
||||||
|
#define MM_BASE_CBM_SERIAL "cbm-serial"
|
||||||
|
|
||||||
|
struct _MMBaseCbm {
|
||||||
|
MmGdbusCbmSkeleton parent;
|
||||||
|
MMBaseCbmPrivate *priv;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _MMBaseCbmClass {
|
||||||
|
MmGdbusCbmSkeletonClass parent;
|
||||||
|
};
|
||||||
|
|
||||||
|
GType mm_base_cbm_get_type (void);
|
||||||
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMBaseCbm, g_object_unref)
|
||||||
|
|
||||||
|
MMBaseCbm *mm_base_cbm_new (MMBaseModem *modem);
|
||||||
|
gboolean mm_base_cbm_take_part (MMBaseCbm *self,
|
||||||
|
MMCbmPart *part,
|
||||||
|
GError **error);
|
||||||
|
MMBaseCbm *mm_base_cbm_new_with_part (MMBaseModem *modem,
|
||||||
|
MMCbmState state,
|
||||||
|
guint max_parts,
|
||||||
|
MMCbmPart *first_part,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
void mm_base_cbm_export (MMBaseCbm *self);
|
||||||
|
void mm_base_cbm_unexport (MMBaseCbm *self);
|
||||||
|
const gchar *mm_base_cbm_get_path (MMBaseCbm *self);
|
||||||
|
|
||||||
|
gboolean mm_base_cbm_has_part_num (MMBaseCbm *self,
|
||||||
|
guint part_num);
|
||||||
|
GList *mm_base_cbm_get_parts (MMBaseCbm *self);
|
||||||
|
guint16 mm_base_cbm_get_serial (MMBaseCbm *self);
|
||||||
|
guint16 mm_base_cbm_get_channel (MMBaseCbm *self);
|
||||||
|
|
||||||
|
gboolean mm_base_cbm_is_complete (MMBaseCbm *self);
|
||||||
|
|
||||||
|
#endif /* MM_BASE_CBM_H */
|
||||||
@@ -0,0 +1,402 @@
|
|||||||
|
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||||
|
/*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details:
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 Guido Günther <agx@sigxcpu.org>
|
||||||
|
*
|
||||||
|
* based on mm-sms-list.c which is
|
||||||
|
*
|
||||||
|
* Copyright (C) 2012 Google, Inc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include <ModemManager.h>
|
||||||
|
#define _LIBMM_INSIDE_MM
|
||||||
|
#include <libmm-glib.h>
|
||||||
|
|
||||||
|
#include "mm-iface-modem-messaging.h"
|
||||||
|
#include "mm-cbm-list.h"
|
||||||
|
#include "mm-base-cbm.h"
|
||||||
|
#include "mm-log-object.h"
|
||||||
|
|
||||||
|
static void log_object_iface_init (MMLogObjectInterface *iface);
|
||||||
|
|
||||||
|
G_DEFINE_TYPE_EXTENDED (MMCbmList, mm_cbm_list, G_TYPE_OBJECT, 0,
|
||||||
|
G_IMPLEMENT_INTERFACE (MM_TYPE_LOG_OBJECT, log_object_iface_init))
|
||||||
|
|
||||||
|
enum {
|
||||||
|
PROP_0,
|
||||||
|
PROP_MODEM,
|
||||||
|
PROP_LAST
|
||||||
|
};
|
||||||
|
static GParamSpec *properties[PROP_LAST];
|
||||||
|
|
||||||
|
enum {
|
||||||
|
SIGNAL_ADDED,
|
||||||
|
SIGNAL_DELETED,
|
||||||
|
SIGNAL_LAST
|
||||||
|
};
|
||||||
|
static guint signals[SIGNAL_LAST];
|
||||||
|
|
||||||
|
struct _MMCbmListPrivate {
|
||||||
|
/* The owner modem */
|
||||||
|
MMBaseModem *modem;
|
||||||
|
/* List of cbm objects */
|
||||||
|
GList *list;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
guint
|
||||||
|
mm_cbm_list_get_count (MMCbmList *self)
|
||||||
|
{
|
||||||
|
return g_list_length (self->priv->list);
|
||||||
|
}
|
||||||
|
|
||||||
|
GStrv
|
||||||
|
mm_cbm_list_get_paths (MMCbmList *self)
|
||||||
|
{
|
||||||
|
GStrv path_list = NULL;
|
||||||
|
GList *l;
|
||||||
|
guint i;
|
||||||
|
|
||||||
|
path_list = g_new0 (gchar *, 1 + g_list_length (self->priv->list));
|
||||||
|
for (i = 0, l = self->priv->list; l; l = g_list_next (l)) {
|
||||||
|
const gchar *path;
|
||||||
|
|
||||||
|
/* Don't try to add NULL paths (not yet exported CBM objects) */
|
||||||
|
path = mm_base_cbm_get_path (MM_BASE_CBM (l->data));
|
||||||
|
if (path)
|
||||||
|
path_list[i++] = g_strdup (path);
|
||||||
|
}
|
||||||
|
|
||||||
|
return path_list;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
mm_cbm_list_delete_cbm_finish (MMCbmList *self,
|
||||||
|
GAsyncResult *res,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
return g_task_propagate_boolean (G_TASK (res), error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static guint
|
||||||
|
cmp_cbm_by_path (MMBaseCbm *cbm,
|
||||||
|
const gchar *path)
|
||||||
|
{
|
||||||
|
return g_strcmp0 (mm_base_cbm_get_path (cbm), path);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
mm_cbm_list_delete_cbm (MMCbmList *self,
|
||||||
|
const gchar *cbm_path,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
g_autoptr (GTask) task = NULL;
|
||||||
|
MMBaseCbm *cbm;
|
||||||
|
GList *l;
|
||||||
|
|
||||||
|
l = g_list_find_custom (self->priv->list,
|
||||||
|
(gpointer)cbm_path,
|
||||||
|
(GCompareFunc)cmp_cbm_by_path);
|
||||||
|
if (!l) {
|
||||||
|
g_task_report_new_error (self,
|
||||||
|
callback,
|
||||||
|
user_data,
|
||||||
|
mm_cbm_list_delete_cbm,
|
||||||
|
MM_CORE_ERROR,
|
||||||
|
MM_CORE_ERROR_NOT_FOUND,
|
||||||
|
"No CBM found with path '%s'",
|
||||||
|
cbm_path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
cbm = MM_BASE_CBM (l->data);
|
||||||
|
|
||||||
|
/* Although this could be done sync we use a task to provide the
|
||||||
|
* async API pattern like other operations */
|
||||||
|
task = g_task_new (self, NULL, callback, user_data);
|
||||||
|
|
||||||
|
self->priv->list = g_list_delete_link (self->priv->list, l);
|
||||||
|
|
||||||
|
mm_base_cbm_unexport (cbm);
|
||||||
|
g_object_unref (cbm);
|
||||||
|
|
||||||
|
g_signal_emit (self, signals[SIGNAL_DELETED], 0, cbm_path);
|
||||||
|
g_task_return_boolean (task, TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
void
|
||||||
|
mm_cbm_list_add_cbm (MMCbmList *self,
|
||||||
|
MMBaseCbm *cbm)
|
||||||
|
{
|
||||||
|
self->priv->list = g_list_prepend (self->priv->list, g_object_ref (cbm));
|
||||||
|
g_signal_emit (self, signals[SIGNAL_ADDED], 0,
|
||||||
|
mm_base_cbm_get_path (cbm),
|
||||||
|
FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
guint16 serial;
|
||||||
|
guint16 channel;
|
||||||
|
} PartIdAndSerial;
|
||||||
|
|
||||||
|
static guint
|
||||||
|
cmp_cbm_by_serial_and_id (MMBaseCbm *cbm,
|
||||||
|
PartIdAndSerial *ctx)
|
||||||
|
{
|
||||||
|
return !(mm_base_cbm_get_serial (cbm) == ctx->serial &&
|
||||||
|
mm_base_cbm_get_channel (cbm) == ctx->channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
take_part (MMCbmList *self,
|
||||||
|
MMCbmPart *part,
|
||||||
|
MMCbmState state,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
GList *l;
|
||||||
|
MMBaseCbm *cbm;
|
||||||
|
PartIdAndSerial cmp;
|
||||||
|
|
||||||
|
cmp = (PartIdAndSerial){
|
||||||
|
.serial = mm_cbm_part_get_serial (part),
|
||||||
|
.channel = mm_cbm_part_get_channel (part),
|
||||||
|
};
|
||||||
|
l = g_list_find_custom (self->priv->list,
|
||||||
|
&cmp,
|
||||||
|
(GCompareFunc)cmp_cbm_by_serial_and_id);
|
||||||
|
if (l) {
|
||||||
|
/* Try to take the part */
|
||||||
|
mm_obj_dbg (self, "found existing multipart CBM object with serial '%u' and id '%u': adding new part",
|
||||||
|
cmp.serial, cmp.channel);
|
||||||
|
return mm_base_cbm_take_part (MM_BASE_CBM (l->data), part, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Create new cbm */
|
||||||
|
cbm = mm_base_cbm_new_with_part (self->priv->modem,
|
||||||
|
state,
|
||||||
|
mm_cbm_part_get_num_parts (part),
|
||||||
|
part,
|
||||||
|
error);
|
||||||
|
if (!cbm)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
mm_obj_dbg (self, "creating new multipart CBM object: need to receive %u parts with serial '%u' and id '%u'",
|
||||||
|
mm_cbm_part_get_num_parts (part), cmp.serial, cmp.channel);
|
||||||
|
|
||||||
|
self->priv->list = g_list_prepend (self->priv->list, cbm);
|
||||||
|
g_signal_emit (self, signals[SIGNAL_ADDED], 0,
|
||||||
|
mm_base_cbm_get_path (cbm),
|
||||||
|
(state == MM_CBM_STATE_RECEIVED ||
|
||||||
|
state == MM_CBM_STATE_RECEIVING));
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
guint16 serial;
|
||||||
|
guint16 channel;
|
||||||
|
guint8 part_num;
|
||||||
|
} PartIdSerialAndNum;
|
||||||
|
|
||||||
|
static guint
|
||||||
|
cmp_cbm_by_serial_id_and_part_num (MMBaseCbm *cbm,
|
||||||
|
PartIdSerialAndNum *ctx)
|
||||||
|
{
|
||||||
|
return !(mm_base_cbm_get_serial (cbm) == ctx->serial &&
|
||||||
|
mm_base_cbm_get_channel (cbm) == ctx->channel &&
|
||||||
|
mm_base_cbm_has_part_num (cbm, ctx->part_num));
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
mm_cbm_list_has_part (MMCbmList *self,
|
||||||
|
guint16 serial,
|
||||||
|
guint16 channel,
|
||||||
|
guint8 part_num)
|
||||||
|
{
|
||||||
|
PartIdSerialAndNum ctx = {
|
||||||
|
.channel = channel,
|
||||||
|
.serial = serial,
|
||||||
|
.part_num = part_num
|
||||||
|
};
|
||||||
|
|
||||||
|
return !!g_list_find_custom (self->priv->list,
|
||||||
|
&ctx,
|
||||||
|
(GCompareFunc)cmp_cbm_by_serial_id_and_part_num);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
mm_cbm_list_take_part (MMCbmList *self,
|
||||||
|
MMCbmPart *part,
|
||||||
|
MMCbmState state,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
/* Ensure we don't have already taken a part with the same index */
|
||||||
|
if (mm_cbm_list_has_part (self,
|
||||||
|
mm_cbm_part_get_serial (part),
|
||||||
|
mm_cbm_part_get_channel (part),
|
||||||
|
mm_cbm_part_get_part_num (part))) {
|
||||||
|
g_set_error (error,
|
||||||
|
MM_CORE_ERROR,
|
||||||
|
MM_CORE_ERROR_FAILED,
|
||||||
|
"A part %u with serial %u and id %u and was already taken",
|
||||||
|
mm_cbm_part_get_part_num (part),
|
||||||
|
mm_cbm_part_get_serial (part),
|
||||||
|
mm_cbm_part_get_channel (part));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return take_part (self, part, state, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static gchar *
|
||||||
|
log_object_build_id (MMLogObject *_self)
|
||||||
|
{
|
||||||
|
return g_strdup ("cbm-list");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
MMCbmList *
|
||||||
|
mm_cbm_list_new (MMBaseModem *modem)
|
||||||
|
{
|
||||||
|
/* Create the object */
|
||||||
|
return g_object_new (MM_TYPE_CBM_LIST,
|
||||||
|
MM_CBM_LIST_MODEM, modem,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
set_property (GObject *object,
|
||||||
|
guint prop_id,
|
||||||
|
const GValue *value,
|
||||||
|
GParamSpec *pspec)
|
||||||
|
{
|
||||||
|
MMCbmList *self = MM_CBM_LIST (object);
|
||||||
|
|
||||||
|
switch (prop_id) {
|
||||||
|
case PROP_MODEM:
|
||||||
|
g_clear_object (&self->priv->modem);
|
||||||
|
self->priv->modem = g_value_dup_object (value);
|
||||||
|
if (self->priv->modem) {
|
||||||
|
/* Set owner ID */
|
||||||
|
mm_log_object_set_owner_id (MM_LOG_OBJECT (self),
|
||||||
|
mm_log_object_get_id (MM_LOG_OBJECT (self->priv->modem)));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
get_property (GObject *object,
|
||||||
|
guint prop_id,
|
||||||
|
GValue *value,
|
||||||
|
GParamSpec *pspec)
|
||||||
|
{
|
||||||
|
MMCbmList *self = MM_CBM_LIST (object);
|
||||||
|
|
||||||
|
switch (prop_id) {
|
||||||
|
case PROP_MODEM:
|
||||||
|
g_value_set_object (value, self->priv->modem);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
mm_cbm_list_init (MMCbmList *self)
|
||||||
|
{
|
||||||
|
/* Initialize private data */
|
||||||
|
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
|
||||||
|
MM_TYPE_CBM_LIST,
|
||||||
|
MMCbmListPrivate);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
dispose (GObject *object)
|
||||||
|
{
|
||||||
|
MMCbmList *self = MM_CBM_LIST (object);
|
||||||
|
|
||||||
|
g_clear_object (&self->priv->modem);
|
||||||
|
g_list_free_full (self->priv->list, g_object_unref);
|
||||||
|
self->priv->list = NULL;
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (mm_cbm_list_parent_class)->dispose (object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
log_object_iface_init (MMLogObjectInterface *iface)
|
||||||
|
{
|
||||||
|
iface->build_id = log_object_build_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
mm_cbm_list_class_init (MMCbmListClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||||
|
|
||||||
|
g_type_class_add_private (object_class, sizeof (MMCbmListPrivate));
|
||||||
|
|
||||||
|
/* Virtual methods */
|
||||||
|
object_class->get_property = get_property;
|
||||||
|
object_class->set_property = set_property;
|
||||||
|
object_class->dispose = dispose;
|
||||||
|
|
||||||
|
/* Properties */
|
||||||
|
properties[PROP_MODEM] =
|
||||||
|
g_param_spec_object (MM_CBM_LIST_MODEM,
|
||||||
|
"Modem",
|
||||||
|
"The Modem which owns this CBM list",
|
||||||
|
MM_TYPE_BASE_MODEM,
|
||||||
|
G_PARAM_READWRITE);
|
||||||
|
g_object_class_install_property (object_class, PROP_MODEM, properties[PROP_MODEM]);
|
||||||
|
|
||||||
|
/* Signals */
|
||||||
|
signals[SIGNAL_ADDED] =
|
||||||
|
g_signal_new (MM_CBM_ADDED,
|
||||||
|
G_OBJECT_CLASS_TYPE (object_class),
|
||||||
|
G_SIGNAL_RUN_FIRST,
|
||||||
|
G_STRUCT_OFFSET (MMCbmListClass, cbm_added),
|
||||||
|
NULL, NULL,
|
||||||
|
g_cclosure_marshal_generic,
|
||||||
|
G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_BOOLEAN);
|
||||||
|
|
||||||
|
signals[SIGNAL_DELETED] =
|
||||||
|
g_signal_new (MM_CBM_DELETED,
|
||||||
|
G_OBJECT_CLASS_TYPE (object_class),
|
||||||
|
G_SIGNAL_RUN_FIRST,
|
||||||
|
G_STRUCT_OFFSET (MMCbmListClass, cbm_deleted),
|
||||||
|
NULL, NULL,
|
||||||
|
g_cclosure_marshal_generic,
|
||||||
|
G_TYPE_NONE, 1, G_TYPE_STRING);
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||||
|
/*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details:
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 Guido Günther <agx@sigxcpu.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MM_CBM_LIST_H
|
||||||
|
#define MM_CBM_LIST_H
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
#include <glib-object.h>
|
||||||
|
|
||||||
|
#include "mm-base-modem.h"
|
||||||
|
#include "mm-base-cbm.h"
|
||||||
|
#include "mm-cbm-part.h"
|
||||||
|
|
||||||
|
#define MM_TYPE_CBM_LIST (mm_cbm_list_get_type ())
|
||||||
|
#define MM_CBM_LIST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_CBM_LIST, MMCbmList))
|
||||||
|
#define MM_CBM_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_CBM_LIST, MMCbmListClass))
|
||||||
|
#define MM_IS_CBM_LIST(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_CBM_LIST))
|
||||||
|
#define MM_IS_CBM_LIST_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_CBM_LIST))
|
||||||
|
#define MM_CBM_LIST_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_CBM_LIST, MMCbmListClass))
|
||||||
|
|
||||||
|
typedef struct _MMCbmList MMCbmList;
|
||||||
|
typedef struct _MMCbmListClass MMCbmListClass;
|
||||||
|
typedef struct _MMCbmListPrivate MMCbmListPrivate;
|
||||||
|
|
||||||
|
#define MM_CBM_LIST_MODEM "cbm-list-modem"
|
||||||
|
|
||||||
|
#define MM_CBM_ADDED "cbm-added"
|
||||||
|
#define MM_CBM_DELETED "cbm-deleted"
|
||||||
|
|
||||||
|
struct _MMCbmList {
|
||||||
|
GObject parent;
|
||||||
|
MMCbmListPrivate *priv;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _MMCbmListClass {
|
||||||
|
GObjectClass parent;
|
||||||
|
|
||||||
|
/* Signals */
|
||||||
|
void (* cbm_added) (MMCbmList *self,
|
||||||
|
const gchar *cbm_path);
|
||||||
|
void (* cbm_deleted) (MMCbmList *self,
|
||||||
|
const gchar *sms_path);
|
||||||
|
};
|
||||||
|
|
||||||
|
GType mm_cbm_list_get_type (void);
|
||||||
|
G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMCbmList, g_object_unref)
|
||||||
|
|
||||||
|
MMCbmList *mm_cbm_list_new (MMBaseModem *modem);
|
||||||
|
|
||||||
|
GStrv mm_cbm_list_get_paths (MMCbmList *self);
|
||||||
|
guint mm_cbm_list_get_count (MMCbmList *self);
|
||||||
|
|
||||||
|
gboolean mm_cbm_list_has_part (MMCbmList *self,
|
||||||
|
guint16 serial,
|
||||||
|
guint16 message_id,
|
||||||
|
guint8 part_num);
|
||||||
|
|
||||||
|
gboolean mm_cbm_list_take_part (MMCbmList *self,
|
||||||
|
MMCbmPart *part,
|
||||||
|
MMCbmState state,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
void mm_cbm_list_add_cbm (MMCbmList *self,
|
||||||
|
MMBaseCbm *cbm);
|
||||||
|
|
||||||
|
void mm_cbm_list_delete_cbm (MMCbmList *self,
|
||||||
|
const gchar *cbm_path,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data);
|
||||||
|
gboolean mm_cbm_list_delete_cbm_finish (MMCbmList *self,
|
||||||
|
GAsyncResult *res,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
#endif /* MM_CBM_LIST_H */
|
||||||
@@ -0,0 +1,654 @@
|
|||||||
|
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||||
|
/*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details:
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 Guido Günther <agx@sigxcpu.org>n
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <ModemManager.h>
|
||||||
|
#define _LIBMM_INSIDE_MM
|
||||||
|
#include <libmm-glib.h>
|
||||||
|
|
||||||
|
#include "mm-iface-modem.h"
|
||||||
|
#include "mm-iface-modem-cell-broadcast.h"
|
||||||
|
#include "mm-cbm-list.h"
|
||||||
|
#include "mm-log-object.h"
|
||||||
|
#include "mm-error-helpers.h"
|
||||||
|
#include "mm-modem-helpers.h"
|
||||||
|
|
||||||
|
#define SUPPORT_CHECKED_TAG "cell-broadcast-support-checked-tag"
|
||||||
|
#define SUPPORTED_TAG "cell-broadcast-supported-tag"
|
||||||
|
|
||||||
|
static GQuark support_checked_quark;
|
||||||
|
static GQuark supported_quark;
|
||||||
|
|
||||||
|
G_DEFINE_INTERFACE (MMIfaceModemCellBroadcast, mm_iface_modem_cell_broadcast, MM_TYPE_IFACE_MODEM)
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
void
|
||||||
|
mm_iface_modem_cell_broadcast_bind_simple_status (MMIfaceModemCellBroadcast *self,
|
||||||
|
MMSimpleStatus *status)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
MmGdbusModemCellBroadcast *skeleton;
|
||||||
|
GDBusMethodInvocation *invocation;
|
||||||
|
MMIfaceModemCellBroadcast *self;
|
||||||
|
gchar *path;
|
||||||
|
} HandleDeleteContext;
|
||||||
|
|
||||||
|
static void
|
||||||
|
handle_delete_context_free (HandleDeleteContext *ctx)
|
||||||
|
{
|
||||||
|
g_object_unref (ctx->skeleton);
|
||||||
|
g_object_unref (ctx->invocation);
|
||||||
|
g_object_unref (ctx->self);
|
||||||
|
g_free (ctx->path);
|
||||||
|
g_slice_free (HandleDeleteContext, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
handle_delete_ready (MMCbmList *list,
|
||||||
|
GAsyncResult *res,
|
||||||
|
HandleDeleteContext *ctx)
|
||||||
|
{
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
if (!mm_cbm_list_delete_cbm_finish (list, res, &error)) {
|
||||||
|
mm_obj_warn (ctx->self, "failed deleting CBM message '%s': %s", ctx->path, error->message);
|
||||||
|
mm_dbus_method_invocation_take_error (ctx->invocation, error);
|
||||||
|
} else {
|
||||||
|
mm_obj_info (ctx->self, "deleted CBM message '%s'", ctx->path);
|
||||||
|
mm_gdbus_modem_cell_broadcast_complete_delete (ctx->skeleton, ctx->invocation);
|
||||||
|
}
|
||||||
|
handle_delete_context_free (ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
handle_delete_auth_ready (MMBaseModem *self,
|
||||||
|
GAsyncResult *res,
|
||||||
|
HandleDeleteContext *ctx)
|
||||||
|
{
|
||||||
|
g_autoptr(MMCbmList) list = NULL;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
if (!mm_base_modem_authorize_finish (self, res, &error)) {
|
||||||
|
mm_dbus_method_invocation_take_error (ctx->invocation, error);
|
||||||
|
handle_delete_context_free (ctx);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We do allow deleting CBMs while enabling or disabling, it doesn't
|
||||||
|
* interfere with the state transition logic to do so. The main reason to allow
|
||||||
|
* this is that during modem enabling we're emitting "Added" signals before we
|
||||||
|
* reach the enabled state, and so users listening to the signal may want to
|
||||||
|
* delete the CBM message as soon as it's read. */
|
||||||
|
if (mm_iface_modem_abort_invocation_if_state_not_reached (MM_IFACE_MODEM (self),
|
||||||
|
ctx->invocation,
|
||||||
|
MM_MODEM_STATE_DISABLING)) {
|
||||||
|
handle_delete_context_free (ctx);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_object_get (self,
|
||||||
|
MM_IFACE_MODEM_CELL_BROADCAST_CBM_LIST, &list,
|
||||||
|
NULL);
|
||||||
|
if (!list) {
|
||||||
|
mm_dbus_method_invocation_return_error_literal (ctx->invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE,
|
||||||
|
"Cannot delete CBM: missing CBM list");
|
||||||
|
handle_delete_context_free (ctx);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mm_obj_info (self, "processing user request to delete CBM message '%s'...", ctx->path);
|
||||||
|
mm_cbm_list_delete_cbm (list,
|
||||||
|
ctx->path,
|
||||||
|
(GAsyncReadyCallback)handle_delete_ready,
|
||||||
|
ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
handle_delete (MmGdbusModemCellBroadcast *skeleton,
|
||||||
|
GDBusMethodInvocation *invocation,
|
||||||
|
const gchar *path,
|
||||||
|
MMIfaceModemCellBroadcast *self)
|
||||||
|
{
|
||||||
|
HandleDeleteContext *ctx;
|
||||||
|
|
||||||
|
ctx = g_slice_new0 (HandleDeleteContext);
|
||||||
|
ctx->skeleton = g_object_ref (skeleton);
|
||||||
|
ctx->invocation = g_object_ref (invocation);
|
||||||
|
ctx->self = g_object_ref (self);
|
||||||
|
ctx->path = g_strdup (path);
|
||||||
|
|
||||||
|
mm_base_modem_authorize (MM_BASE_MODEM (self),
|
||||||
|
invocation,
|
||||||
|
MM_AUTHORIZATION_CELL_BROADCAST,
|
||||||
|
(GAsyncReadyCallback)handle_delete_auth_ready,
|
||||||
|
ctx);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
handle_list (MmGdbusModemCellBroadcast *skeleton,
|
||||||
|
GDBusMethodInvocation *invocation,
|
||||||
|
MMIfaceModemCellBroadcast *self)
|
||||||
|
{
|
||||||
|
g_auto(GStrv) paths = NULL;
|
||||||
|
g_autoptr(MMCbmList) list = NULL;
|
||||||
|
|
||||||
|
if (mm_iface_modem_abort_invocation_if_state_not_reached (MM_IFACE_MODEM (self),
|
||||||
|
invocation,
|
||||||
|
MM_MODEM_STATE_ENABLED))
|
||||||
|
return TRUE;
|
||||||
|
|
||||||
|
g_object_get (self,
|
||||||
|
MM_IFACE_MODEM_CELL_BROADCAST_CBM_LIST, &list,
|
||||||
|
NULL);
|
||||||
|
if (!list) {
|
||||||
|
mm_dbus_method_invocation_return_error_literal (invocation, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE,
|
||||||
|
"Cannot list CBM: missing CBM list");
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
mm_obj_info (self, "processing user request to list CBM messages...");
|
||||||
|
paths = mm_cbm_list_get_paths (list);
|
||||||
|
mm_gdbus_modem_cell_broadcast_complete_list (skeleton,
|
||||||
|
invocation,
|
||||||
|
(const gchar *const *)paths);
|
||||||
|
mm_obj_info (self, "reported %u CBM messages available", paths ? g_strv_length (paths) : 0);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
MMBaseCbm *
|
||||||
|
mm_iface_modem_cell_broadcast_create_cbm (MMIfaceModemCellBroadcast *self)
|
||||||
|
{
|
||||||
|
g_assert (MM_IFACE_MODEM_CELL_BROADCAST_GET_IFACE (self)->create_cbm != NULL);
|
||||||
|
|
||||||
|
return MM_IFACE_MODEM_CELL_BROADCAST_GET_IFACE (self)->create_cbm (self);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
typedef struct _InitializationContext InitializationContext;
|
||||||
|
static void interface_initialization_step (GTask *task);
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
INITIALIZATION_STEP_FIRST,
|
||||||
|
INITIALIZATION_STEP_CHECK_SUPPORT,
|
||||||
|
INITIALIZATION_STEP_FAIL_IF_UNSUPPORTED,
|
||||||
|
INITIALIZATION_STEP_LAST
|
||||||
|
} InitializationStep;
|
||||||
|
|
||||||
|
struct _InitializationContext {
|
||||||
|
MmGdbusModemCellBroadcast *skeleton;
|
||||||
|
InitializationStep step;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
initialization_context_free (InitializationContext *ctx)
|
||||||
|
{
|
||||||
|
g_object_unref (ctx->skeleton);
|
||||||
|
g_free (ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
mm_iface_modem_cell_broadcast_initialize_finish (MMIfaceModemCellBroadcast *self,
|
||||||
|
GAsyncResult *res,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
return g_task_propagate_boolean (G_TASK (res), error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
check_support_ready (MMIfaceModemCellBroadcast *self,
|
||||||
|
GAsyncResult *res,
|
||||||
|
GTask *task)
|
||||||
|
{
|
||||||
|
InitializationContext *ctx;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
if (!MM_IFACE_MODEM_CELL_BROADCAST_GET_IFACE (self)->check_support_finish (self,
|
||||||
|
res,
|
||||||
|
&error)) {
|
||||||
|
if (error) {
|
||||||
|
/* This error shouldn't be treated as critical */
|
||||||
|
mm_obj_dbg (self, "cell broadcast support check failed: %s", error->message);
|
||||||
|
g_error_free (error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* CellBroadcast is supported! */
|
||||||
|
g_object_set_qdata (G_OBJECT (self),
|
||||||
|
supported_quark,
|
||||||
|
GUINT_TO_POINTER (TRUE));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Go on to next step */
|
||||||
|
ctx = g_task_get_task_data (task);
|
||||||
|
ctx->step++;
|
||||||
|
interface_initialization_step (task);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
interface_initialization_step (GTask *task)
|
||||||
|
{
|
||||||
|
MMIfaceModemCellBroadcast *self;
|
||||||
|
InitializationContext *ctx;
|
||||||
|
|
||||||
|
/* Don't run new steps if we're cancelled */
|
||||||
|
if (g_task_return_error_if_cancelled (task)) {
|
||||||
|
g_object_unref (task);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self = g_task_get_source_object (task);
|
||||||
|
ctx = g_task_get_task_data (task);
|
||||||
|
|
||||||
|
switch (ctx->step) {
|
||||||
|
case INITIALIZATION_STEP_FIRST:
|
||||||
|
/* Setup quarks if we didn't do it before */
|
||||||
|
if (G_UNLIKELY (!support_checked_quark))
|
||||||
|
support_checked_quark = (g_quark_from_static_string (
|
||||||
|
SUPPORT_CHECKED_TAG));
|
||||||
|
if (G_UNLIKELY (!supported_quark))
|
||||||
|
supported_quark = (g_quark_from_static_string (
|
||||||
|
SUPPORTED_TAG));
|
||||||
|
|
||||||
|
ctx->step++;
|
||||||
|
/* fall through */
|
||||||
|
|
||||||
|
case INITIALIZATION_STEP_CHECK_SUPPORT:
|
||||||
|
if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (self),
|
||||||
|
support_checked_quark))) {
|
||||||
|
/* Set the checked flag so that we don't run it again */
|
||||||
|
g_object_set_qdata (G_OBJECT (self),
|
||||||
|
support_checked_quark,
|
||||||
|
GUINT_TO_POINTER (TRUE));
|
||||||
|
/* Initially, assume we don't support it */
|
||||||
|
g_object_set_qdata (G_OBJECT (self),
|
||||||
|
supported_quark,
|
||||||
|
GUINT_TO_POINTER (FALSE));
|
||||||
|
|
||||||
|
if (MM_IFACE_MODEM_CELL_BROADCAST_GET_IFACE (self)->check_support &&
|
||||||
|
MM_IFACE_MODEM_CELL_BROADCAST_GET_IFACE (self)->check_support_finish) {
|
||||||
|
MM_IFACE_MODEM_CELL_BROADCAST_GET_IFACE (self)->check_support (
|
||||||
|
self,
|
||||||
|
(GAsyncReadyCallback)check_support_ready,
|
||||||
|
task);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If there is no implementation to check support, assume we DON'T
|
||||||
|
* support it. */
|
||||||
|
}
|
||||||
|
ctx->step++;
|
||||||
|
/* fall through */
|
||||||
|
|
||||||
|
case INITIALIZATION_STEP_FAIL_IF_UNSUPPORTED:
|
||||||
|
if (!GPOINTER_TO_UINT (g_object_get_qdata (G_OBJECT (self),
|
||||||
|
supported_quark))) {
|
||||||
|
g_task_return_new_error (task,
|
||||||
|
MM_CORE_ERROR,
|
||||||
|
MM_CORE_ERROR_UNSUPPORTED,
|
||||||
|
"CellBroadcast not supported");
|
||||||
|
g_object_unref (task);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ctx->step++;
|
||||||
|
/* fall through */
|
||||||
|
|
||||||
|
case INITIALIZATION_STEP_LAST:
|
||||||
|
/* We are done without errors! */
|
||||||
|
g_signal_connect (ctx->skeleton,
|
||||||
|
"handle-delete",
|
||||||
|
G_CALLBACK (handle_delete),
|
||||||
|
self);
|
||||||
|
g_signal_connect (ctx->skeleton,
|
||||||
|
"handle-list",
|
||||||
|
G_CALLBACK (handle_list),
|
||||||
|
self);
|
||||||
|
|
||||||
|
/* Finally, export the new interface */
|
||||||
|
mm_gdbus_object_skeleton_set_modem_cell_broadcast (MM_GDBUS_OBJECT_SKELETON (self),
|
||||||
|
MM_GDBUS_MODEM_CELL_BROADCAST (ctx->skeleton));
|
||||||
|
|
||||||
|
g_task_return_boolean (task, TRUE);
|
||||||
|
g_object_unref (task);
|
||||||
|
return;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_assert_not_reached ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
mm_iface_modem_cell_broadcast_initialize (MMIfaceModemCellBroadcast *self,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
InitializationContext *ctx;
|
||||||
|
MmGdbusModemCellBroadcast *skeleton = NULL;
|
||||||
|
GTask *task;
|
||||||
|
|
||||||
|
/* Did we already create it? */
|
||||||
|
g_object_get (self,
|
||||||
|
MM_IFACE_MODEM_CELL_BROADCAST_DBUS_SKELETON, &skeleton,
|
||||||
|
NULL);
|
||||||
|
if (!skeleton) {
|
||||||
|
skeleton = mm_gdbus_modem_cell_broadcast_skeleton_new ();
|
||||||
|
g_object_set (self,
|
||||||
|
MM_IFACE_MODEM_CELL_BROADCAST_DBUS_SKELETON, skeleton,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Perform async initialization here */
|
||||||
|
|
||||||
|
ctx = g_new0 (InitializationContext, 1);
|
||||||
|
ctx->step = INITIALIZATION_STEP_FIRST;
|
||||||
|
ctx->skeleton = skeleton;
|
||||||
|
|
||||||
|
task = g_task_new (self, cancellable, callback, user_data);
|
||||||
|
g_task_set_task_data (task, ctx, (GDestroyNotify)initialization_context_free);
|
||||||
|
|
||||||
|
interface_initialization_step (task);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
mm_iface_modem_cell_broadcast_shutdown (MMIfaceModemCellBroadcast *self)
|
||||||
|
{
|
||||||
|
/* Unexport DBus interface and remove the skeleton */
|
||||||
|
mm_gdbus_object_skeleton_set_modem_cell_broadcast (MM_GDBUS_OBJECT_SKELETON (self), NULL);
|
||||||
|
g_object_set (self,
|
||||||
|
MM_IFACE_MODEM_CELL_BROADCAST_DBUS_SKELETON, NULL,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static void
|
||||||
|
update_message_list (MmGdbusModemCellBroadcast *skeleton,
|
||||||
|
MMCbmList *list)
|
||||||
|
{
|
||||||
|
gchar **paths;
|
||||||
|
|
||||||
|
paths = mm_cbm_list_get_paths (list);
|
||||||
|
mm_gdbus_modem_cell_broadcast_set_cell_broadcasts (skeleton, (const gchar *const *)paths);
|
||||||
|
g_strfreev (paths);
|
||||||
|
|
||||||
|
g_dbus_interface_skeleton_flush (G_DBUS_INTERFACE_SKELETON (skeleton));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
cbm_added (MMCbmList *list,
|
||||||
|
const gchar *cbm_path,
|
||||||
|
gboolean received,
|
||||||
|
MmGdbusModemCellBroadcast *skeleton)
|
||||||
|
{
|
||||||
|
update_message_list (skeleton, list);
|
||||||
|
mm_gdbus_modem_cell_broadcast_emit_added (skeleton, cbm_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
cbm_deleted (MMCbmList *list,
|
||||||
|
const gchar *cbm_path,
|
||||||
|
MmGdbusModemCellBroadcast *skeleton)
|
||||||
|
{
|
||||||
|
update_message_list (skeleton, list);
|
||||||
|
mm_gdbus_modem_cell_broadcast_emit_deleted (skeleton, cbm_path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
typedef struct _EnablingContext EnablingContext;
|
||||||
|
static void interface_enabling_step (GTask *task);
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
ENABLING_STEP_FIRST,
|
||||||
|
ENABLING_STEP_SETUP_UNSOLICITED_EVENTS,
|
||||||
|
ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS,
|
||||||
|
ENABLING_STEP_LAST
|
||||||
|
} EnablingStep;
|
||||||
|
|
||||||
|
struct _EnablingContext {
|
||||||
|
EnablingStep step;
|
||||||
|
MmGdbusModemCellBroadcast *skeleton;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
enabling_context_free (EnablingContext *ctx)
|
||||||
|
{
|
||||||
|
if (ctx->skeleton)
|
||||||
|
g_object_unref (ctx->skeleton);
|
||||||
|
g_free (ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
mm_iface_modem_cell_broadcast_enable_finish (MMIfaceModemCellBroadcast *self,
|
||||||
|
GAsyncResult *res,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
return g_task_propagate_boolean (G_TASK (res), error);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
setup_unsolicited_events_ready (MMIfaceModemCellBroadcast *self,
|
||||||
|
GAsyncResult *res,
|
||||||
|
GTask *task)
|
||||||
|
{
|
||||||
|
EnablingContext *ctx;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
MM_IFACE_MODEM_CELL_BROADCAST_GET_IFACE (self)->setup_unsolicited_events_finish (self, res, &error);
|
||||||
|
if (error) {
|
||||||
|
g_task_return_error (task, error);
|
||||||
|
g_object_unref (task);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Go on to next step */
|
||||||
|
ctx = g_task_get_task_data (task);
|
||||||
|
ctx->step++;
|
||||||
|
interface_enabling_step (task);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
enable_unsolicited_events_ready (MMIfaceModemCellBroadcast *self,
|
||||||
|
GAsyncResult *res,
|
||||||
|
GTask *task)
|
||||||
|
{
|
||||||
|
EnablingContext *ctx;
|
||||||
|
GError *error = NULL;
|
||||||
|
|
||||||
|
/* Not critical! */
|
||||||
|
if (!MM_IFACE_MODEM_CELL_BROADCAST_GET_IFACE (self)->enable_unsolicited_events_finish (self, res, &error)) {
|
||||||
|
mm_obj_dbg (self, "Can't enable unsolicited events: %s", error->message);
|
||||||
|
g_error_free (error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Go on with next step */
|
||||||
|
ctx = g_task_get_task_data (task);
|
||||||
|
ctx->step++;
|
||||||
|
interface_enabling_step (task);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
interface_enabling_step (GTask *task)
|
||||||
|
{
|
||||||
|
MMIfaceModemCellBroadcast *self;
|
||||||
|
EnablingContext *ctx;
|
||||||
|
|
||||||
|
/* Don't run new steps if we're cancelled */
|
||||||
|
if (g_task_return_error_if_cancelled (task)) {
|
||||||
|
g_object_unref (task);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
self = g_task_get_source_object (task);
|
||||||
|
ctx = g_task_get_task_data (task);
|
||||||
|
|
||||||
|
switch (ctx->step) {
|
||||||
|
case ENABLING_STEP_FIRST: {
|
||||||
|
g_autoptr (MMCbmList) list = NULL;
|
||||||
|
|
||||||
|
list = mm_cbm_list_new (MM_BASE_MODEM (self));
|
||||||
|
g_object_set (self,
|
||||||
|
MM_IFACE_MODEM_CELL_BROADCAST_CBM_LIST, list,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
/* Connect to list's signals */
|
||||||
|
g_signal_connect (list,
|
||||||
|
MM_CBM_ADDED,
|
||||||
|
G_CALLBACK (cbm_added),
|
||||||
|
ctx->skeleton);
|
||||||
|
g_signal_connect (list,
|
||||||
|
MM_CBM_DELETED,
|
||||||
|
G_CALLBACK (cbm_deleted),
|
||||||
|
ctx->skeleton);
|
||||||
|
ctx->step++;
|
||||||
|
} /* fall through */
|
||||||
|
|
||||||
|
case ENABLING_STEP_SETUP_UNSOLICITED_EVENTS:
|
||||||
|
/* Allow setting up unsolicited events */
|
||||||
|
if (MM_IFACE_MODEM_CELL_BROADCAST_GET_IFACE (self)->setup_unsolicited_events &&
|
||||||
|
MM_IFACE_MODEM_CELL_BROADCAST_GET_IFACE (self)->setup_unsolicited_events_finish) {
|
||||||
|
MM_IFACE_MODEM_CELL_BROADCAST_GET_IFACE (self)->setup_unsolicited_events (
|
||||||
|
self,
|
||||||
|
(GAsyncReadyCallback)setup_unsolicited_events_ready,
|
||||||
|
task);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ctx->step++;
|
||||||
|
/* fall through */
|
||||||
|
|
||||||
|
case ENABLING_STEP_ENABLE_UNSOLICITED_EVENTS:
|
||||||
|
/* Allow enabling unsolicited events */
|
||||||
|
if (MM_IFACE_MODEM_CELL_BROADCAST_GET_IFACE (self)->enable_unsolicited_events &&
|
||||||
|
MM_IFACE_MODEM_CELL_BROADCAST_GET_IFACE (self)->enable_unsolicited_events_finish) {
|
||||||
|
MM_IFACE_MODEM_CELL_BROADCAST_GET_IFACE (self)->enable_unsolicited_events (
|
||||||
|
self,
|
||||||
|
(GAsyncReadyCallback)enable_unsolicited_events_ready,
|
||||||
|
task);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ctx->step++;
|
||||||
|
/* fall through */
|
||||||
|
|
||||||
|
case ENABLING_STEP_LAST:
|
||||||
|
/* We are done without errors! */
|
||||||
|
g_task_return_boolean (task, TRUE);
|
||||||
|
g_object_unref (task);
|
||||||
|
return;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_assert_not_reached ();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
mm_iface_modem_cell_broadcast_enable (MMIfaceModemCellBroadcast *self,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data)
|
||||||
|
{
|
||||||
|
EnablingContext *ctx;
|
||||||
|
GTask *task;
|
||||||
|
|
||||||
|
ctx = g_new0 (EnablingContext, 1);
|
||||||
|
ctx->step = ENABLING_STEP_FIRST;
|
||||||
|
|
||||||
|
task = g_task_new (self, cancellable, callback, user_data);
|
||||||
|
g_task_set_task_data (task, ctx, (GDestroyNotify)enabling_context_free);
|
||||||
|
|
||||||
|
g_object_get (self,
|
||||||
|
MM_IFACE_MODEM_CELL_BROADCAST_DBUS_SKELETON, &ctx->skeleton,
|
||||||
|
NULL);
|
||||||
|
if (!ctx->skeleton) {
|
||||||
|
g_task_return_new_error (task,
|
||||||
|
MM_CORE_ERROR,
|
||||||
|
MM_CORE_ERROR_FAILED,
|
||||||
|
"Can't get interface skeleton");
|
||||||
|
g_object_unref (task);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface_enabling_step (task);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
gboolean
|
||||||
|
mm_iface_modem_cell_broadcast_take_part (MMIfaceModemCellBroadcast *self,
|
||||||
|
MMCbmPart *cbm_part,
|
||||||
|
MMCbmState state)
|
||||||
|
{
|
||||||
|
g_autoptr(MMCbmList) list = NULL;
|
||||||
|
g_autoptr(GError) error = NULL;
|
||||||
|
gboolean added = FALSE;
|
||||||
|
|
||||||
|
g_object_get (self,
|
||||||
|
MM_IFACE_MODEM_CELL_BROADCAST_CBM_LIST, &list,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
if (list) {
|
||||||
|
added = mm_cbm_list_take_part (list, cbm_part, state, &error);
|
||||||
|
if (!added)
|
||||||
|
mm_obj_dbg (self, "Can't take part in CBM list: %s", error->message);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If part wasn't taken, we need to free the part ourselves */
|
||||||
|
if (!added)
|
||||||
|
mm_cbm_part_free (cbm_part);
|
||||||
|
|
||||||
|
return added;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*****************************************************************************/
|
||||||
|
|
||||||
|
static void
|
||||||
|
mm_iface_modem_cell_broadcast_default_init (MMIfaceModemCellBroadcastInterface *iface)
|
||||||
|
{
|
||||||
|
static gsize initialized = 0;
|
||||||
|
|
||||||
|
if (!g_once_init_enter (&initialized))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* Properties */
|
||||||
|
g_object_interface_install_property (
|
||||||
|
iface,
|
||||||
|
g_param_spec_object (MM_IFACE_MODEM_CELL_BROADCAST_DBUS_SKELETON,
|
||||||
|
"CellBroadcast DBus skeleton",
|
||||||
|
"DBus skeleton for the CellBroadcast interface",
|
||||||
|
MM_GDBUS_TYPE_MODEM_CELL_BROADCAST_SKELETON,
|
||||||
|
G_PARAM_READWRITE));
|
||||||
|
|
||||||
|
g_object_interface_install_property (
|
||||||
|
iface,
|
||||||
|
g_param_spec_object (MM_IFACE_MODEM_CELL_BROADCAST_CBM_LIST,
|
||||||
|
"CBM list",
|
||||||
|
"List of CBM objects managed in the interface",
|
||||||
|
MM_TYPE_CBM_LIST,
|
||||||
|
G_PARAM_READWRITE));
|
||||||
|
|
||||||
|
g_once_init_leave (&initialized, 1);
|
||||||
|
}
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||||
|
/*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details:
|
||||||
|
*
|
||||||
|
* Copyright (C) 2024 Guido Günther <agx@sigxcpu.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef MM_IFACE_MODEM_CELLBROADCAST_H
|
||||||
|
#define MM_IFACE_MODEM_CELLBROADCAST_H
|
||||||
|
|
||||||
|
#include <glib-object.h>
|
||||||
|
#include <gio/gio.h>
|
||||||
|
|
||||||
|
#define _LIBMM_INSIDE_MM
|
||||||
|
#include <libmm-glib.h>
|
||||||
|
|
||||||
|
#include "mm-cbm-part.h"
|
||||||
|
#include "mm-base-cbm.h"
|
||||||
|
|
||||||
|
#define MM_TYPE_IFACE_MODEM_CELL_BROADCAST mm_iface_modem_cell_broadcast_get_type ()
|
||||||
|
G_DECLARE_INTERFACE (MMIfaceModemCellBroadcast, mm_iface_modem_cell_broadcast, MM, IFACE_MODEM_CELL_BROADCAST, MMIfaceModem)
|
||||||
|
|
||||||
|
#define MM_IFACE_MODEM_CELL_BROADCAST_DBUS_SKELETON "iface-modem-cell-broadcast-dbus-skeleton"
|
||||||
|
#define MM_IFACE_MODEM_CELL_BROADCAST_CBM_LIST "iface-modem-cell-broadcast-cbm-list"
|
||||||
|
|
||||||
|
struct _MMIfaceModemCellBroadcastInterface {
|
||||||
|
GTypeInterface g_iface;
|
||||||
|
|
||||||
|
/* Check for CellBroadcast support (async) */
|
||||||
|
void (* check_support) (MMIfaceModemCellBroadcast *self,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data);
|
||||||
|
gboolean (* check_support_finish) (MMIfaceModemCellBroadcast *self,
|
||||||
|
GAsyncResult *res,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
/* Asynchronous setting up unsolicited CellBroadcast reception events */
|
||||||
|
void (* setup_unsolicited_events) (MMIfaceModemCellBroadcast *self,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data);
|
||||||
|
gboolean (* setup_unsolicited_events_finish) (MMIfaceModemCellBroadcast *self,
|
||||||
|
GAsyncResult *res,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
/* Asynchronous cleaning up of unsolicited CellBroadcast reception events */
|
||||||
|
void (* cleanup_unsolicited_events) (MMIfaceModemCellBroadcast *self,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data);
|
||||||
|
gboolean (* cleanup_unsolicited_events_finish) (MMIfaceModemCellBroadcast *self,
|
||||||
|
GAsyncResult *res,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
/* Asynchronous enabling unsolicited CellBroadcast reception events */
|
||||||
|
void (* enable_unsolicited_events) (MMIfaceModemCellBroadcast *self,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data);
|
||||||
|
gboolean (* enable_unsolicited_events_finish) (MMIfaceModemCellBroadcast *self,
|
||||||
|
GAsyncResult *res,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
/* Asynchronous disabling unsolicited CellBroadcast reception events */
|
||||||
|
void (* disable_unsolicited_events) (MMIfaceModemCellBroadcast *self,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data);
|
||||||
|
gboolean (* disable_unsolicited_events_finish) (MMIfaceModemCellBroadcast *self,
|
||||||
|
GAsyncResult *res,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
/* Create Cbm objects */
|
||||||
|
MMBaseCbm * (* create_cbm) (MMIfaceModemCellBroadcast *self);
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Initialize CellBroadcast interface (async) */
|
||||||
|
void mm_iface_modem_cell_broadcast_initialize (MMIfaceModemCellBroadcast *self,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data);
|
||||||
|
gboolean mm_iface_modem_cell_broadcast_initialize_finish (MMIfaceModemCellBroadcast *self,
|
||||||
|
GAsyncResult *res,
|
||||||
|
GError **error);
|
||||||
|
/* Enable CellBroadcast interface (async) */
|
||||||
|
void mm_iface_modem_cell_broadcast_enable (MMIfaceModemCellBroadcast *self,
|
||||||
|
GCancellable *cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data);
|
||||||
|
gboolean mm_iface_modem_cell_broadcast_enable_finish (MMIfaceModemCellBroadcast *self,
|
||||||
|
GAsyncResult *res,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
|
/* Disable CellBroadcast interface (async) */
|
||||||
|
void mm_iface_modem_cell_broadcast_disable (MMIfaceModemCellBroadcast *self,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data);
|
||||||
|
gboolean mm_iface_modem_cell_broadcast_disable_finish (MMIfaceModemCellBroadcast *self,
|
||||||
|
GAsyncResult *res,
|
||||||
|
GError **error);
|
||||||
|
/* Shutdown CellBroadcast interface */
|
||||||
|
void mm_iface_modem_cell_broadcast_shutdown (MMIfaceModemCellBroadcast *self);
|
||||||
|
|
||||||
|
/* Bind properties for simple GetStatus() */
|
||||||
|
void mm_iface_modem_cell_broadcast_bind_simple_status (MMIfaceModemCellBroadcast *self,
|
||||||
|
MMSimpleStatus *status);
|
||||||
|
|
||||||
|
/* Report new CBM part */
|
||||||
|
gboolean mm_iface_modem_cell_broadcast_take_part (MMIfaceModemCellBroadcast *self,
|
||||||
|
MMCbmPart *cbm_part,
|
||||||
|
MMCbmState state);
|
||||||
|
|
||||||
|
/* CBM creation */
|
||||||
|
MMBaseCbm *mm_iface_modem_cell_broadcast_create_cbm (MMIfaceModemCellBroadcast *self);
|
||||||
|
|
||||||
|
#endif /* MM_IFACE_MODEM_CELLBROADCAST_H */
|
||||||
Reference in New Issue
Block a user