[ofono-binder] Initial implementation. JB#55524
The logic is based on ofono-ril-plugin. Only interfaces up to and including IRadio@1.2 are fully supported, although bits and pieces of IRadio@1.4 are already there. Full support for IRadio@1.4 will be added later as a separate task. For now, only the standard (as in "defined by Google") IRadio interfaces are used. Support for vendor-specific extensions is planned and will be added later.
This commit is contained in:
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
*~
|
||||
build
|
||||
unit/coverage/*.gcov
|
||||
unit/coverage/report
|
||||
2
LICENSE
2
LICENSE
@@ -1,4 +1,4 @@
|
||||
Copyright (C) 2021 Jolla Ltd.
|
||||
Copyright (C) 2021-2022 Jolla Ltd.
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License version 2 as
|
||||
|
||||
222
Makefile
Normal file
222
Makefile
Normal file
@@ -0,0 +1,222 @@
|
||||
# -*- Mode: makefile-gmake -*-
|
||||
|
||||
.PHONY: clean all debug release test
|
||||
.PHONY: debug_lib release_lib coverage_lib
|
||||
.PHONY: print_debug_lib print_release_lib print_coverage_lib
|
||||
|
||||
#
|
||||
# Required packages
|
||||
#
|
||||
# ofono.pc adds -export-symbols-regex linker option which doesn't work
|
||||
# on all platforms.
|
||||
#
|
||||
|
||||
LDPKGS = libgbinder-radio libgbinder libmce-glib libglibutil gobject-2.0 glib-2.0
|
||||
PKGS = ofono $(LDPKGS)
|
||||
|
||||
#
|
||||
# Default target
|
||||
#
|
||||
|
||||
all: debug release
|
||||
|
||||
#
|
||||
# Library name
|
||||
#
|
||||
|
||||
NAME = binderplugin
|
||||
LIB_NAME = $(NAME)
|
||||
LIB_SONAME = $(LIB_NAME).so
|
||||
LIB = $(LIB_SONAME)
|
||||
STATIC_LIB = $(NAME).a
|
||||
|
||||
#
|
||||
# Sources
|
||||
#
|
||||
|
||||
SRC = \
|
||||
binder_base.c \
|
||||
binder_call_barring.c \
|
||||
binder_call_forwarding.c \
|
||||
binder_call_settings.c \
|
||||
binder_call_volume.c \
|
||||
binder_cbs.c \
|
||||
binder_cell_info.c \
|
||||
binder_connman.c \
|
||||
binder_data.c \
|
||||
binder_devinfo.c \
|
||||
binder_devmon.c \
|
||||
binder_devmon_combine.c \
|
||||
binder_devmon_ds.c \
|
||||
binder_devmon_if.c \
|
||||
binder_gprs.c \
|
||||
binder_gprs_context.c \
|
||||
binder_logger.c \
|
||||
binder_modem.c \
|
||||
binder_netreg.c \
|
||||
binder_network.c \
|
||||
binder_radio.c \
|
||||
binder_radio_caps.c \
|
||||
binder_radio_settings.c \
|
||||
binder_sim.c \
|
||||
binder_sim_card.c \
|
||||
binder_sim_settings.c \
|
||||
binder_sms.c \
|
||||
binder_stk.c \
|
||||
binder_ussd.c \
|
||||
binder_util.c \
|
||||
binder_voicecall.c \
|
||||
binder_plugin.c
|
||||
|
||||
#
|
||||
# Directories
|
||||
#
|
||||
|
||||
SRC_DIR = src
|
||||
BUILD_DIR = build
|
||||
DEBUG_BUILD_DIR = $(BUILD_DIR)/debug
|
||||
RELEASE_BUILD_DIR = $(BUILD_DIR)/release
|
||||
COVERAGE_BUILD_DIR = $(BUILD_DIR)/coverage
|
||||
|
||||
#
|
||||
# Tools and flags
|
||||
#
|
||||
|
||||
CC = $(CROSS_COMPILE)gcc
|
||||
LD = $(CC)
|
||||
WARNINGS = -Wall
|
||||
BASE_FLAGS = -fPIC -fvisibility=hidden
|
||||
FULL_CFLAGS = $(BASE_FLAGS) $(CFLAGS) $(DEFINES) $(WARNINGS) -MMD -MP \
|
||||
$(shell pkg-config --cflags $(PKGS))
|
||||
FULL_LDFLAGS = $(BASE_FLAGS) $(LDFLAGS) -shared \
|
||||
$(shell pkg-config --libs $(LDPKGS))
|
||||
DEBUG_FLAGS = -g
|
||||
RELEASE_FLAGS =
|
||||
COVERAGE_FLAGS = -g
|
||||
|
||||
KEEP_SYMBOLS ?= 0
|
||||
ifneq ($(KEEP_SYMBOLS),0)
|
||||
RELEASE_FLAGS += -g
|
||||
endif
|
||||
|
||||
DEBUG_LDFLAGS = $(FULL_LDFLAGS) $(DEBUG_FLAGS)
|
||||
RELEASE_LDFLAGS = $(FULL_LDFLAGS) $(RELEASE_FLAGS)
|
||||
DEBUG_CFLAGS = $(FULL_CFLAGS) $(DEBUG_FLAGS) -DDEBUG
|
||||
RELEASE_CFLAGS = $(FULL_CFLAGS) $(RELEASE_FLAGS) -O2
|
||||
COVERAGE_CFLAGS = $(FULL_CFLAGS) $(COVERAGE_FLAGS) --coverage
|
||||
|
||||
#
|
||||
# Files
|
||||
#
|
||||
|
||||
DEBUG_OBJS = $(SRC:%.c=$(DEBUG_BUILD_DIR)/%.o)
|
||||
RELEASE_OBJS = $(SRC:%.c=$(RELEASE_BUILD_DIR)/%.o)
|
||||
COVERAGE_OBJS = $(SRC:%.c=$(COVERAGE_BUILD_DIR)/%.o)
|
||||
|
||||
DEBUG_SO = $(DEBUG_BUILD_DIR)/$(LIB)
|
||||
RELEASE_SO = $(RELEASE_BUILD_DIR)/$(LIB)
|
||||
|
||||
DEBUG_LIB = $(DEBUG_BUILD_DIR)/$(STATIC_LIB)
|
||||
RELEASE_LIB = $(RELEASE_BUILD_DIR)/$(STATIC_LIB)
|
||||
COVERAGE_LIB = $(COVERAGE_BUILD_DIR)/$(STATIC_LIB)
|
||||
|
||||
#
|
||||
# Dependencies
|
||||
#
|
||||
|
||||
DEPS = $(DEBUG_OBJS:%.o=%.d) $(RELEASE_OBJS:%.o=%.d) $(COVERAGE_OBJS:%.o=%.d)
|
||||
ifneq ($(MAKECMDGOALS),clean)
|
||||
ifneq ($(strip $(DEPS)),)
|
||||
-include $(DEPS)
|
||||
endif
|
||||
endif
|
||||
|
||||
$(DEBUG_OBJS) $(DEBUG_SO): | $(DEBUG_BUILD_DIR)
|
||||
$(RELEASE_OBJS) $(RELEASE_SO): | $(RELEASE_BUILD_DIR)
|
||||
$(COVERAGE_OBJS) $(COVERAGE_LIB): | $(COVERAGE_BUILD_DIR)
|
||||
|
||||
#
|
||||
# Rules
|
||||
#
|
||||
|
||||
debug: $(DEBUG_SO)
|
||||
|
||||
release: $(RELEASE_SO)
|
||||
|
||||
debug_lib: $(DEBUG_LIB)
|
||||
|
||||
release_lib: $(RELEASE_LIB)
|
||||
|
||||
coverage_lib: $(COVERAGE_LIB)
|
||||
|
||||
print_debug_lib:
|
||||
@echo $(DEBUG_LIB)
|
||||
|
||||
print_release_lib:
|
||||
@echo $(RELEASE_LIB)
|
||||
|
||||
print_coverage_lib:
|
||||
@echo $(COVERAGE_LIB)
|
||||
|
||||
clean:
|
||||
make -C unit clean
|
||||
rm -f *~ $(SRC_DIR)/*~ rpm/*~
|
||||
rm -fr $(BUILD_DIR)
|
||||
|
||||
test:
|
||||
make -C unit test
|
||||
|
||||
$(DEBUG_BUILD_DIR):
|
||||
mkdir -p $@
|
||||
|
||||
$(RELEASE_BUILD_DIR):
|
||||
mkdir -p $@
|
||||
|
||||
$(COVERAGE_BUILD_DIR):
|
||||
mkdir -p $@
|
||||
|
||||
$(DEBUG_BUILD_DIR)/%.o : $(SRC_DIR)/%.c
|
||||
$(CC) -c $(DEBUG_CFLAGS) -MT"$@" -MF"$(@:%.o=%.d)" $< -o $@
|
||||
|
||||
$(RELEASE_BUILD_DIR)/%.o : $(SRC_DIR)/%.c
|
||||
$(CC) -c $(RELEASE_CFLAGS) -MT"$@" -MF"$(@:%.o=%.d)" $< -o $@
|
||||
|
||||
$(COVERAGE_BUILD_DIR)/%.o : $(SRC_DIR)/%.c
|
||||
$(CC) -c $(COVERAGE_CFLAGS) -MT"$@" -MF"$(@:%.o=%.d)" $< -o $@
|
||||
|
||||
$(DEBUG_SO): $(DEBUG_OBJS)
|
||||
$(LD) $(DEBUG_OBJS) $(DEBUG_LDFLAGS) -o $@
|
||||
|
||||
$(RELEASE_SO): $(RELEASE_OBJS)
|
||||
$(LD) $(RELEASE_OBJS) $(RELEASE_LDFLAGS) -o $@
|
||||
ifeq ($(KEEP_SYMBOLS),0)
|
||||
$(STRIP) $@
|
||||
endif
|
||||
|
||||
$(DEBUG_LIB): $(DEBUG_OBJS)
|
||||
$(AR) rc $@ $?
|
||||
ranlib $@
|
||||
|
||||
$(RELEASE_LIB): $(RELEASE_OBJS)
|
||||
$(AR) rc $@ $?
|
||||
ranlib $@
|
||||
|
||||
$(COVERAGE_LIB): $(COVERAGE_OBJS)
|
||||
$(AR) rc $@ $?
|
||||
ranlib $@
|
||||
|
||||
#
|
||||
# Install
|
||||
#
|
||||
|
||||
PLUGINDIR ?= $$(pkg-config ofono --variable=plugindir)
|
||||
ABS_PLUGINDIR := $(shell echo /$(PLUGINDIR) | sed -r 's|/+|/|g')
|
||||
|
||||
INSTALL = install
|
||||
INSTALL_PLUGIN_DIR = $(DESTDIR)$(ABS_PLUGINDIR)
|
||||
|
||||
install: $(INSTALL_PLUGIN_DIR)
|
||||
$(INSTALL) -m 755 $(RELEASE_SO) $(INSTALL_PLUGIN_DIR)
|
||||
|
||||
$(INSTALL_PLUGIN_DIR):
|
||||
$(INSTALL) -d $@
|
||||
8
README
8
README
@@ -2,3 +2,11 @@ Binder based ofono plugin.
|
||||
|
||||
Integrates Sailfish OS fork of ofono with Android adaptations
|
||||
which support IRadio family of binder interfaces.
|
||||
|
||||
This plugin is a replacement for ofono-ril-plugin.
|
||||
|
||||
For reliable startup, /etc/ofono/binder.conf has to list all
|
||||
expected slots, for example:
|
||||
|
||||
[Settings]
|
||||
ExpectSlots = slot1,slot2
|
||||
|
||||
151
binder.conf
Normal file
151
binder.conf
Normal file
@@ -0,0 +1,151 @@
|
||||
# This is a sample configuration file for ofono binder driver
|
||||
#
|
||||
# This file is expected to be installed in /etc/ofono
|
||||
#
|
||||
# The convention is that the keys which can only appear in the [Settings]
|
||||
# section start with the upper case, those which may appear in the [slotX]
|
||||
# i.e. slot specific section start with lower case.
|
||||
#
|
||||
# Slot specific settings may also appear in the [Settings] section in which
|
||||
# case they apply to all modems. The exceptions are "path" and "slot" values
|
||||
# which must be unique and therefore must appear in the section(s) for the
|
||||
# respective slot(s).
|
||||
#
|
||||
# By default, the list of slots is fetched from hwservicemanager managing
|
||||
# services at /dev/hwbinder
|
||||
#
|
||||
|
||||
[Settings]
|
||||
|
||||
#
|
||||
# Binder device to talk to.
|
||||
#
|
||||
# Default /dev/hwbinder
|
||||
#
|
||||
#Device=/dev/hwbinder
|
||||
|
||||
# User and group for the ofono process. RIL clients are typically
|
||||
# expected to run under radio:radio.
|
||||
#
|
||||
# Default radio:radio
|
||||
#
|
||||
#Identity=radio:radio
|
||||
|
||||
# If the phone has more than one SIM slot, the 3G/LTE module may be
|
||||
# shared by all modems, meaning that only one of the slots can use
|
||||
# 3G/LTE. In order to "hand 4G over" to the other slot, the modem
|
||||
# currently using 3G/LTE has to drop to GSM, release 3G/LTE module
|
||||
# and only then 3G/LTE can be used by the other modem. This setting
|
||||
# allows to disable this behaviour (say, if your phone has independent
|
||||
# 3G/LTE modules for each slot or you don't need 4G for both slots).
|
||||
# Obviously, it only has any effect if you have more than one SIM.
|
||||
#
|
||||
# Defaults to true (switch the current data modem to 2G when changing
|
||||
# the data modems)
|
||||
#
|
||||
#3GLTEHandover=true
|
||||
|
||||
# If this option is set, preferred technology is limited for non-data
|
||||
# slots. If set to none, preferred technology doesn't depend on whether
|
||||
# the slot is selected for data or not.
|
||||
#
|
||||
# Possible values are none, gsm and umts
|
||||
#
|
||||
# Default umts
|
||||
#
|
||||
#MaxNonDataMode=umts
|
||||
|
||||
# RIL_REQUEST_SET_RADIO_CAPABILITY may or may not be supported by your RIL.
|
||||
# This option allows you to forcibly enable or disable use of this request.
|
||||
# It's involved in 3G/LTE handover between the modems, meaning that it only
|
||||
# makes sense if you have more than one slot.
|
||||
#
|
||||
# Possible values are auto, on and off
|
||||
#
|
||||
# Default auto (enable if supported)
|
||||
#
|
||||
#SetRadioCapability=auto
|
||||
|
||||
# Comma-separated list of slots to expect. These slots are added to the
|
||||
# list the slots reported by hwservicemanager. Duplicates are ignored, i.e.
|
||||
# the same slot doesn't get added twice.
|
||||
#
|
||||
# It's recommended that this list is specified, otherwise ofono may start
|
||||
# before the modem adaptation and miss some or even all slots.
|
||||
#
|
||||
# Default empty
|
||||
#
|
||||
#ExpectSlots=
|
||||
|
||||
# Comma-separated list of slots to ignore. Glob-style patterns are supported.
|
||||
# Doesn't apply to the expected slots defined by ExpectSlots
|
||||
#
|
||||
# Default empty
|
||||
#
|
||||
#IgnoreSlots=
|
||||
|
||||
#
|
||||
# SLOT SPECIFIC ENTRIES
|
||||
#
|
||||
# Config groups are named after the slots, e.g.
|
||||
#
|
||||
# [slot1]
|
||||
|
||||
# Since IRadio API doesn't provide a standard way of querying the number
|
||||
# of remaining pin retries, some implementations (namely Qualcomm) allow
|
||||
# to query the retry count by sending the empty pin. If your implementation
|
||||
# actually does check the empty pin (and decrements the retry count) then
|
||||
# you should turn this feature off.
|
||||
#
|
||||
# Default true
|
||||
#
|
||||
#emptyPinQuery=true
|
||||
|
||||
# setDataAllowed request may or may not be supported by your modem.
|
||||
# This option allows you to disable use of this request.
|
||||
# Possible values are on and off
|
||||
#
|
||||
# Default on
|
||||
#
|
||||
#allowDataReq=on
|
||||
|
||||
# Enables use of setDataProfile requests.
|
||||
#
|
||||
# Default true
|
||||
#
|
||||
#useDataProfiles=true
|
||||
|
||||
# Comma-separated signal strength range, in dBm.
|
||||
#
|
||||
# These values are used for translating dBm values returned by the modem in
|
||||
# LTE mode into signal strength percentage. If you are getting significantly
|
||||
# different signal strength readings in GSM and LTE modes, you may need to
|
||||
# tweak those.
|
||||
#
|
||||
# Default -100,-60
|
||||
#
|
||||
#signalStrengthRange=-100,-60
|
||||
|
||||
# With some modems, network scan returns strange operator names, i.e.
|
||||
# numeric MCC+MNC values or the same name for all operators (which is
|
||||
# actually SPN fetched from the SIM). Such strange names can be replaced
|
||||
# with operator names from MBPI database, based on the operator's MCC and
|
||||
# MNC. That may not be 100% accurate, though.
|
||||
#
|
||||
# Default false (i.e. trust the modem to report the actual names)
|
||||
#
|
||||
#replaceStrangeOperatorNames=false
|
||||
|
||||
# Configures device state tracking (basically, power saving strategy).
|
||||
# Possible values are:
|
||||
#
|
||||
# ds = sendDeviceState mechanism
|
||||
# if = setIndicationFilter mechanism
|
||||
# all = All of the above
|
||||
# none = Disable device state management
|
||||
#
|
||||
# Note that one can specify a combination of methods, e.g. ds+if
|
||||
#
|
||||
# Default all
|
||||
#
|
||||
#deviceStateTracking=all
|
||||
77
rpm/ofono-binder-plugin.spec
Normal file
77
rpm/ofono-binder-plugin.spec
Normal file
@@ -0,0 +1,77 @@
|
||||
Name: ofono-binder-plugin
|
||||
|
||||
Version: 1.0.0
|
||||
Release: 1
|
||||
Summary: Binder based ofono plugin
|
||||
License: GPLv2
|
||||
URL: https://github.com/mer-hybris/ofono-binder-plugin
|
||||
Source: %{name}-%{version}.tar.bz2
|
||||
|
||||
%define libglibutil_version 1.0.61
|
||||
%define libgbinder_version 1.1.15
|
||||
%define libgbinder_radio_version 1.4.8
|
||||
%define libmce_version 1.0.6
|
||||
%define ofono_version 1.28+git2
|
||||
|
||||
BuildRequires: pkgconfig
|
||||
BuildRequires: ofono-devel >= %{ofono_version}
|
||||
BuildRequires: pkgconfig(libgbinder) >= %{libgbinder_version}
|
||||
BuildRequires: pkgconfig(libgbinder-radio) >= %{libgbinder_radio_version}
|
||||
BuildRequires: pkgconfig(libglibutil) >= %{libglibutil_version}
|
||||
BuildRequires: pkgconfig(libmce-glib) >= %{libmce_version}
|
||||
|
||||
# license macro requires rpm >= 4.11
|
||||
BuildRequires: pkgconfig(rpm)
|
||||
%define license_support %(pkg-config --exists 'rpm >= 4.11'; echo $?)
|
||||
|
||||
Requires: ofono >= %{ofono_version}
|
||||
Requires: libgbinder >= %{libgbinder_version}
|
||||
Requires: libgbinder-radio >= %{libgbinder_radio_version}
|
||||
Requires: libglibutil >= %{libglibutil_version}
|
||||
Requires: libmce-glib >= %{libmce_version}
|
||||
|
||||
Conflicts: ofono-ril-plugin
|
||||
Obsoletes: ofono-ril-plugin
|
||||
Conflicts: ofono-ril-binder-plugin
|
||||
Obsoletes: ofono-ril-binder-plugin
|
||||
|
||||
%define plugin_dir %(pkg-config ofono --variable=plugindir)
|
||||
%define config_dir /etc/ofono/
|
||||
|
||||
%description
|
||||
Binder plugin for Sailfish OS fork of ofono
|
||||
|
||||
%package -n ofono-configs-binder
|
||||
Summary: Package to provide default binder configs for ofono
|
||||
Provides: ofono-configs
|
||||
|
||||
%description -n ofono-configs-binder
|
||||
This package provides default configs for ofono
|
||||
|
||||
%prep
|
||||
%setup -q -n %{name}-%{version}
|
||||
|
||||
%build
|
||||
make %{_smp_mflags} PLUGINDIR=%{plugin_dir} KEEP_SYMBOLS=1 release
|
||||
|
||||
%check
|
||||
make test
|
||||
|
||||
%install
|
||||
rm -rf %{buildroot}
|
||||
make DESTDIR=%{buildroot} install
|
||||
mkdir -p %{buildroot}%{config_dir}
|
||||
install -m 644 binder.conf %{buildroot}%{config_dir}
|
||||
|
||||
%files
|
||||
%dir %{plugin_dir}
|
||||
%defattr(-,root,root,-)
|
||||
%{plugin_dir}/binderplugin.so
|
||||
%if %{license_support} == 0
|
||||
%license LICENSE
|
||||
%endif
|
||||
|
||||
%files -n ofono-configs-binder
|
||||
%dir %{config_dir}
|
||||
%defattr(-,root,root,-)
|
||||
%config %{config_dir}/binder.conf
|
||||
189
src/binder_base.c
Normal file
189
src/binder_base.c
Normal file
@@ -0,0 +1,189 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "binder_base.h"
|
||||
#include "binder_log.h"
|
||||
|
||||
typedef
|
||||
void
|
||||
(*BinderBasePropertyFunc)(
|
||||
gpointer source,
|
||||
guint property,
|
||||
gpointer user_data);
|
||||
|
||||
typedef struct binder_base_closure {
|
||||
GCClosure cclosure;
|
||||
BinderBasePropertyFunc callback;
|
||||
gpointer user_data;
|
||||
} BinderBaseClosure;
|
||||
|
||||
#define binder_base_closure_new() ((BinderBaseClosure*) \
|
||||
g_closure_new_simple(sizeof(BinderBaseClosure), NULL))
|
||||
|
||||
G_DEFINE_ABSTRACT_TYPE(BinderBase, binder_base, G_TYPE_OBJECT)
|
||||
#define GET_CLASS(obj) G_TYPE_INSTANCE_GET_CLASS((obj), \
|
||||
BINDER_TYPE_BASE, BinderBaseClass)
|
||||
|
||||
#define SIGNAL_PROPERTY_CHANGED_NAME "binder-base-property-changed"
|
||||
#define SIGNAL_PROPERTY_DETAIL "%x"
|
||||
#define SIGNAL_PROPERTY_DETAIL_MAX_LEN (8)
|
||||
|
||||
enum binder_base_signal {
|
||||
SIGNAL_PROPERTY_CHANGED,
|
||||
SIGNAL_COUNT
|
||||
};
|
||||
|
||||
static guint binder_base_signals[SIGNAL_COUNT];
|
||||
static GQuark binder_base_property_quarks[BINDER_BASE_MAX_PROPERTIES];
|
||||
|
||||
static
|
||||
GQuark
|
||||
binder_base_property_quark(
|
||||
guint property)
|
||||
{
|
||||
GASSERT(property < BINDER_BASE_MAX_PROPERTIES);
|
||||
/* For ANY property this function is expected to return zero */
|
||||
if (property > 0 && G_LIKELY(property < BINDER_BASE_MAX_PROPERTIES)) {
|
||||
const int i = property - 1;
|
||||
|
||||
if (G_UNLIKELY(!binder_base_property_quarks[i])) {
|
||||
char buf[SIGNAL_PROPERTY_DETAIL_MAX_LEN + 1];
|
||||
|
||||
snprintf(buf, sizeof(buf), SIGNAL_PROPERTY_DETAIL, property);
|
||||
buf[sizeof(buf) - 1] = 0;
|
||||
binder_base_property_quarks[i] = g_quark_from_string(buf);
|
||||
}
|
||||
return binder_base_property_quarks[i];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_base_property_changed(
|
||||
BinderBase* self,
|
||||
guint property,
|
||||
BinderBaseClosure* closure)
|
||||
{
|
||||
const BinderBaseClass* klass = GET_CLASS(self);
|
||||
|
||||
closure->callback(((guint8*)self) + klass->public_offset, property,
|
||||
closure->user_data);
|
||||
}
|
||||
|
||||
/*==========================================================================*
|
||||
* API
|
||||
*==========================================================================*/
|
||||
|
||||
gulong
|
||||
binder_base_add_property_handler(
|
||||
BinderBase* self,
|
||||
guint property,
|
||||
GCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
if (G_LIKELY(callback)) {
|
||||
/*
|
||||
* We can't directly connect the provided callback because
|
||||
* it expects the first parameter to point to public part
|
||||
* of the object but glib will call it with BinderBase as
|
||||
* the first parameter. binder_base_property_changed() will
|
||||
* do the conversion.
|
||||
*/
|
||||
BinderBaseClosure* closure = binder_base_closure_new();
|
||||
GCClosure* cc = &closure->cclosure;
|
||||
|
||||
cc->closure.data = closure;
|
||||
cc->callback = G_CALLBACK(binder_base_property_changed);
|
||||
closure->callback = (BinderBasePropertyFunc)callback;
|
||||
closure->user_data = user_data;
|
||||
|
||||
return g_signal_connect_closure_by_id(self,
|
||||
binder_base_signals[SIGNAL_PROPERTY_CHANGED],
|
||||
binder_base_property_quark(property), &cc->closure, FALSE);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
binder_base_queue_property_change(
|
||||
BinderBase* self,
|
||||
guint property)
|
||||
{
|
||||
self->queued_signals |= BINDER_BASE_PROPERTY_BIT(property);
|
||||
}
|
||||
|
||||
void
|
||||
binder_base_emit_property_change(
|
||||
BinderBase* self,
|
||||
guint property)
|
||||
{
|
||||
binder_base_queue_property_change(self, property);
|
||||
binder_base_emit_queued_signals(self);
|
||||
}
|
||||
|
||||
void
|
||||
binder_base_emit_queued_signals(
|
||||
BinderBase* self)
|
||||
{
|
||||
guint p;
|
||||
|
||||
/* Signal handlers may release references to this object */
|
||||
g_object_ref(self);
|
||||
|
||||
/* Emit the signals */
|
||||
for (p = 0; self->queued_signals && p < BINDER_BASE_MAX_PROPERTIES; p++) {
|
||||
if (self->queued_signals & BINDER_BASE_PROPERTY_BIT(p)) {
|
||||
self->queued_signals &= ~BINDER_BASE_PROPERTY_BIT(p);
|
||||
g_signal_emit(self, binder_base_signals[SIGNAL_PROPERTY_CHANGED],
|
||||
binder_base_property_quark(p), p);
|
||||
}
|
||||
}
|
||||
|
||||
/* Release the temporary reference */
|
||||
g_object_unref(self);
|
||||
}
|
||||
|
||||
/*==========================================================================*
|
||||
* Internals
|
||||
*==========================================================================*/
|
||||
|
||||
static
|
||||
void
|
||||
binder_base_init(
|
||||
BinderBase* self)
|
||||
{
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_base_class_init(
|
||||
BinderBaseClass* klass)
|
||||
{
|
||||
/* By default assume that public part immediately follows BinderBase */
|
||||
klass->public_offset = sizeof(BinderBase);
|
||||
binder_base_signals[SIGNAL_PROPERTY_CHANGED] =
|
||||
g_signal_new(SIGNAL_PROPERTY_CHANGED_NAME, G_OBJECT_CLASS_TYPE(klass),
|
||||
G_SIGNAL_RUN_FIRST | G_SIGNAL_DETAILED, 0, NULL, NULL, NULL,
|
||||
G_TYPE_NONE, 1, G_TYPE_UINT);
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
80
src/binder_base.h
Normal file
80
src/binder_base.h
Normal file
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef BINDER_BASE_H
|
||||
#define BINDER_BASE_H
|
||||
|
||||
#include "binder_types.h"
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
typedef struct binder_base_class {
|
||||
GObjectClass object;
|
||||
int public_offset;
|
||||
} BinderBaseClass;
|
||||
|
||||
typedef struct binder_base {
|
||||
GObject object;
|
||||
gsize queued_signals;
|
||||
} BinderBase;
|
||||
|
||||
BINDER_INTERNAL GType binder_base_get_type(void);
|
||||
#define BINDER_TYPE_BASE (binder_base_get_type())
|
||||
#define BINDER_BASE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), \
|
||||
BINDER_TYPE_BASE, BinderBaseClass))
|
||||
|
||||
typedef enum binder_base_property {
|
||||
BINDER_BASE_PROPERTY_ANY = 0,
|
||||
BINDER_BASE_MAX_PROPERTIES = 8
|
||||
} BINDER_BASE_PROPERTY;
|
||||
|
||||
#define BINDER_BASE_PROPERTY_BIT(property) (1 << (property - 1))
|
||||
#define BINDER_BASE_ASSERT_COUNT(count) \
|
||||
G_STATIC_ASSERT((int)count <= (int)BINDER_BASE_MAX_PROPERTIES)
|
||||
|
||||
gulong
|
||||
binder_base_add_property_handler(
|
||||
BinderBase* base,
|
||||
guint property,
|
||||
GCallback callback,
|
||||
gpointer user_data)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_base_queue_property_change(
|
||||
BinderBase* base,
|
||||
guint property)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_base_emit_property_change(
|
||||
BinderBase* base,
|
||||
guint property)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_base_emit_queued_signals(
|
||||
BinderBase* base)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
#endif /* BINDER_BASE_H */
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
382
src/binder_call_barring.c
Normal file
382
src/binder_call_barring.c
Normal file
@@ -0,0 +1,382 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "binder_call_barring.h"
|
||||
#include "binder_log.h"
|
||||
#include "binder_modem.h"
|
||||
#include "binder_sim_card.h"
|
||||
#include "binder_util.h"
|
||||
|
||||
#include <ofono/call-barring.h>
|
||||
#include <ofono/log.h>
|
||||
|
||||
#include <radio_request.h>
|
||||
#include <radio_request_group.h>
|
||||
|
||||
#include <gbinder_reader.h>
|
||||
#include <gbinder_writer.h>
|
||||
|
||||
typedef struct binder_call_barring {
|
||||
struct ofono_call_barring* b;
|
||||
BinderSimCard* card;
|
||||
RadioRequestGroup* g;
|
||||
char* log_prefix;
|
||||
guint register_id;
|
||||
} BinderCallBarring;
|
||||
|
||||
typedef struct binder_call_barring_callback_data {
|
||||
BinderCallBarring* self;
|
||||
union call_barring_cb {
|
||||
ofono_call_barring_query_cb_t query;
|
||||
ofono_call_barring_set_cb_t set;
|
||||
BinderCallback ptr;
|
||||
} cb;
|
||||
gpointer data;
|
||||
} BinderCallBarringCbData;
|
||||
|
||||
#define DBG_(self,fmt,args...) DBG("%s" fmt, (self)->log_prefix, ##args)
|
||||
|
||||
static inline BinderCallBarring*
|
||||
binder_call_barring_get_data(struct ofono_call_barring* b)
|
||||
{ return ofono_call_barring_get_data(b); }
|
||||
|
||||
static
|
||||
BinderCallBarringCbData*
|
||||
binder_call_barring_callback_data_new(
|
||||
BinderCallBarring* self,
|
||||
BinderCallback cb,
|
||||
void* data)
|
||||
{
|
||||
BinderCallBarringCbData* cbd = g_slice_new0(BinderCallBarringCbData);
|
||||
|
||||
cbd->self = self;
|
||||
cbd->cb.ptr = cb;
|
||||
cbd->data = data;
|
||||
return cbd;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_call_barring_callback_data_free(
|
||||
gpointer cbd)
|
||||
{
|
||||
g_slice_free(BinderCallBarringCbData, cbd);
|
||||
}
|
||||
|
||||
static
|
||||
gboolean
|
||||
binder_call_barring_query_ok(
|
||||
const BinderCallBarringCbData* cbd,
|
||||
const GBinderReader* args)
|
||||
{
|
||||
GBinderReader reader;
|
||||
gint32 response;
|
||||
|
||||
/*
|
||||
* getFacilityLockForAppResponse(RadioResponseInfo, int32_t response);
|
||||
*
|
||||
* response - the TS 27.007 service class bit vector of services
|
||||
* for which the specified barring facility is active.
|
||||
* 0 means "disabled for all"
|
||||
*/
|
||||
gbinder_reader_copy(&reader, args);
|
||||
if (gbinder_reader_read_int32(&reader, &response)) {
|
||||
struct ofono_error err;
|
||||
|
||||
DBG_(cbd->self, "Active services: %d", response);
|
||||
cbd->cb.query(binder_error_ok(&err), response, cbd->data);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_call_barring_query_cb(
|
||||
RadioRequest* req,
|
||||
RADIO_TX_STATUS status,
|
||||
RADIO_RESP resp,
|
||||
RADIO_ERROR error,
|
||||
const GBinderReader* args,
|
||||
void* user_data)
|
||||
{
|
||||
struct ofono_error err;
|
||||
const BinderCallBarringCbData* cbd = user_data;
|
||||
|
||||
if (status == RADIO_TX_STATUS_OK) {
|
||||
if (resp == RADIO_RESP_GET_FACILITY_LOCK_FOR_APP) {
|
||||
if (error == RADIO_ERROR_NONE) {
|
||||
if (binder_call_barring_query_ok(cbd, args)) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
ofono_warn("Call Barring query error %d", error);
|
||||
}
|
||||
} else {
|
||||
ofono_error("Unexpected getFacilityLockForApp response %d", resp);
|
||||
}
|
||||
}
|
||||
cbd->cb.query(binder_error_failure(&err), 0, cbd->data);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_call_barring_query(
|
||||
struct ofono_call_barring* b,
|
||||
const char* lock,
|
||||
int cls,
|
||||
ofono_call_barring_query_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
BinderCallBarring* self = ofono_call_barring_get_data(b);
|
||||
|
||||
/*
|
||||
* getFacilityLockForApp(int32_t serial, string facility,
|
||||
* string password, int32_t serviceClass, string appId);
|
||||
*/
|
||||
GBinderWriter writer;
|
||||
RadioRequest* req = radio_request_new2(self->g,
|
||||
RADIO_REQ_GET_FACILITY_LOCK_FOR_APP, &writer,
|
||||
binder_call_barring_query_cb,
|
||||
binder_call_barring_callback_data_free,
|
||||
binder_call_barring_callback_data_new(self, BINDER_CB(cb), data));
|
||||
|
||||
DBG_(self, "lock: %s, services to query: 0x%02x", lock, cls);
|
||||
binder_append_hidl_string(&writer, lock); /* facility */
|
||||
binder_append_hidl_string(&writer, ""); /* password */
|
||||
gbinder_writer_append_int32(&writer, cls); /* serviceClass */
|
||||
binder_append_hidl_string(&writer, binder_sim_card_app_aid(self->card));
|
||||
/* appId */
|
||||
radio_request_submit(req);
|
||||
radio_request_unref(req);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_call_barring_set_cb(
|
||||
RadioRequest* req,
|
||||
RADIO_TX_STATUS status,
|
||||
RADIO_RESP resp,
|
||||
RADIO_ERROR error,
|
||||
const GBinderReader* args,
|
||||
void* user_data)
|
||||
{
|
||||
struct ofono_error err;
|
||||
const BinderCallBarringCbData* cbd = user_data;
|
||||
ofono_call_barring_set_cb_t cb = cbd->cb.set;
|
||||
|
||||
if (status == RADIO_TX_STATUS_OK) {
|
||||
if (resp == RADIO_RESP_SET_FACILITY_LOCK_FOR_APP) {
|
||||
/*
|
||||
* setFacilityLockForAppResponse(RadioResponseInfo, int32_t retry);
|
||||
*
|
||||
* retry - the number of retries remaining, or -1 if unknown
|
||||
*/
|
||||
if (error == RADIO_ERROR_NONE) {
|
||||
cb(binder_error_ok(&err), cbd->data);
|
||||
return;
|
||||
} else {
|
||||
ofono_error("Call Barring Set error %d", error);
|
||||
}
|
||||
} else {
|
||||
ofono_error("Unexpected setFacilityLockForApp response %d", resp);
|
||||
}
|
||||
}
|
||||
cb(binder_error_failure(&err), cbd->data);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_call_barring_set(
|
||||
struct ofono_call_barring* b,
|
||||
const char* lock,
|
||||
int enable,
|
||||
const char* passwd,
|
||||
int cls,
|
||||
ofono_call_barring_set_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
BinderCallBarring* self = ofono_call_barring_get_data(b);
|
||||
|
||||
/*
|
||||
* setFacilityLockForApp(int32_t serial, string facility, bool lockState,
|
||||
* string password, int32_t serviceClass, string appId);
|
||||
*/
|
||||
GBinderWriter writer;
|
||||
RadioRequest* req = radio_request_new2(self->g,
|
||||
RADIO_REQ_SET_FACILITY_LOCK_FOR_APP, &writer,
|
||||
binder_call_barring_set_cb,
|
||||
binder_call_barring_callback_data_free,
|
||||
binder_call_barring_callback_data_new(self, BINDER_CB(cb), data));
|
||||
|
||||
DBG_(self, "lock: %s, enable: %i, bearer class: %i", lock, enable, cls);
|
||||
|
||||
binder_append_hidl_string(&writer, lock); /* facility */
|
||||
gbinder_writer_append_bool(&writer, enable); /* lockState */
|
||||
binder_append_hidl_string(&writer, passwd); /* password */
|
||||
gbinder_writer_append_int32(&writer, cls); /* serviceClass */
|
||||
binder_append_hidl_string(&writer, binder_sim_card_app_aid(self->card));
|
||||
/* appId */
|
||||
radio_request_submit(req);
|
||||
radio_request_unref(req);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_call_barring_set_passwd_cb(
|
||||
RadioRequest* req,
|
||||
RADIO_TX_STATUS status,
|
||||
RADIO_RESP resp,
|
||||
RADIO_ERROR error,
|
||||
const GBinderReader* args,
|
||||
void* user_data)
|
||||
{
|
||||
struct ofono_error err;
|
||||
const BinderCallBarringCbData* cbd = user_data;
|
||||
ofono_call_barring_set_cb_t cb = cbd->cb.set;
|
||||
|
||||
if (status == RADIO_TX_STATUS_OK) {
|
||||
if (resp == RADIO_RESP_SET_BARRING_PASSWORD) {
|
||||
if (error == RADIO_ERROR_NONE) {
|
||||
cb(binder_error_ok(&err), cbd->data);
|
||||
return;
|
||||
} else {
|
||||
ofono_error("Call Barring Set PW error %d", error);
|
||||
}
|
||||
} else {
|
||||
ofono_error("Unexpected setBarringPassword response %d", resp);
|
||||
}
|
||||
}
|
||||
cb(binder_error_failure(&err), cbd->data);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_call_barring_set_passwd(
|
||||
struct ofono_call_barring* b,
|
||||
const char* lock,
|
||||
const char* old_passwd,
|
||||
const char* new_passwd,
|
||||
ofono_call_barring_set_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
BinderCallBarring* self = ofono_call_barring_get_data(b);
|
||||
|
||||
/*
|
||||
* setBarringPassword(int32_t serial, string facility,
|
||||
* string oldPassword, string newPassword);
|
||||
*/
|
||||
GBinderWriter writer;
|
||||
RadioRequest* req = radio_request_new2(self->g,
|
||||
RADIO_REQ_SET_BARRING_PASSWORD, &writer,
|
||||
binder_call_barring_set_passwd_cb,
|
||||
binder_call_barring_callback_data_free,
|
||||
binder_call_barring_callback_data_new(self, BINDER_CB(cb), data));
|
||||
|
||||
DBG_(self, "");
|
||||
binder_append_hidl_string(&writer, lock); /* facility */
|
||||
binder_append_hidl_string(&writer, old_passwd); /* oldPassword */
|
||||
binder_append_hidl_string(&writer, new_passwd); /* newPassword */
|
||||
|
||||
radio_request_submit(req);
|
||||
radio_request_unref(req);
|
||||
}
|
||||
|
||||
static
|
||||
gboolean
|
||||
binder_call_barring_register(
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderCallBarring* self = user_data;
|
||||
|
||||
GASSERT(self->register_id);
|
||||
self->register_id = 0;
|
||||
ofono_call_barring_register(self->b);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
binder_call_barring_probe(
|
||||
struct ofono_call_barring* b,
|
||||
unsigned int vendor,
|
||||
void* data)
|
||||
{
|
||||
BinderModem* modem = binder_modem_get_data(data);
|
||||
BinderCallBarring* self = g_new0(struct binder_call_barring, 1);
|
||||
|
||||
self->b = b;
|
||||
self->card = binder_sim_card_ref(modem->sim_card);
|
||||
self->g = radio_request_group_new(modem->client);
|
||||
self->log_prefix = binder_dup_prefix(modem->log_prefix);
|
||||
self->register_id = g_idle_add(binder_call_barring_register, self);
|
||||
|
||||
DBG_(self, "");
|
||||
ofono_call_barring_set_data(b, self);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_call_barring_remove(
|
||||
struct ofono_call_barring* b)
|
||||
{
|
||||
BinderCallBarring* self = binder_call_barring_get_data(b);
|
||||
|
||||
DBG_(self, "");
|
||||
if (self->register_id) {
|
||||
g_source_remove(self->register_id);
|
||||
}
|
||||
binder_sim_card_unref(self->card);
|
||||
radio_request_group_cancel(self->g);
|
||||
radio_request_group_unref(self->g);
|
||||
g_free(self->log_prefix);
|
||||
g_free(self);
|
||||
|
||||
ofono_call_barring_set_data(b, NULL);
|
||||
}
|
||||
|
||||
/*==========================================================================*
|
||||
* API
|
||||
*==========================================================================*/
|
||||
|
||||
static const struct ofono_call_barring_driver binder_call_barring_driver = {
|
||||
.name = BINDER_DRIVER,
|
||||
.probe = binder_call_barring_probe,
|
||||
.remove = binder_call_barring_remove,
|
||||
.query = binder_call_barring_query,
|
||||
.set = binder_call_barring_set,
|
||||
.set_passwd = binder_call_barring_set_passwd
|
||||
};
|
||||
|
||||
void
|
||||
binder_call_barring_init()
|
||||
{
|
||||
ofono_call_barring_driver_register(&binder_call_barring_driver);
|
||||
}
|
||||
|
||||
void
|
||||
binder_call_barring_cleanup()
|
||||
{
|
||||
ofono_call_barring_driver_unregister(&binder_call_barring_driver);
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
37
src/binder_call_barring.h
Normal file
37
src/binder_call_barring.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef BINDER_CALL_BARRING_H
|
||||
#define BINDER_CALL_BARRING_H
|
||||
|
||||
#include "binder_types.h"
|
||||
|
||||
void
|
||||
binder_call_barring_init(void)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_call_barring_cleanup(void)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
#endif /* BINDER_CALL_BARRING_H */
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
398
src/binder_call_forwarding.c
Normal file
398
src/binder_call_forwarding.c
Normal file
@@ -0,0 +1,398 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "binder_call_forwarding.h"
|
||||
#include "binder_log.h"
|
||||
#include "binder_modem.h"
|
||||
#include "binder_util.h"
|
||||
|
||||
#include <ofono/call-forwarding.h>
|
||||
#include <ofono/log.h>
|
||||
|
||||
#include <radio_request.h>
|
||||
#include <radio_request_group.h>
|
||||
|
||||
#include <gbinder_reader.h>
|
||||
#include <gbinder_writer.h>
|
||||
|
||||
typedef struct binder_call_forwarding {
|
||||
struct ofono_call_forwarding* f;
|
||||
RadioRequestGroup* g;
|
||||
char* log_prefix;
|
||||
guint register_id;
|
||||
} BinderCallForwarding;
|
||||
|
||||
typedef struct binder_call_forwarding_cbd {
|
||||
union call_forwarding_cb {
|
||||
ofono_call_forwarding_query_cb_t query;
|
||||
ofono_call_forwarding_set_cb_t set;
|
||||
BinderCallback ptr;
|
||||
} cb;
|
||||
gpointer data;
|
||||
} BinderCallForwardingCbData;
|
||||
|
||||
#define CF_TIME_DEFAULT (0)
|
||||
|
||||
#define DBG_(self,fmt,args...) DBG("%s" fmt, (self)->log_prefix, ##args)
|
||||
|
||||
static inline BinderCallForwarding*
|
||||
binder_call_forwarding_get_data(struct ofono_call_forwarding* f)
|
||||
{ return ofono_call_forwarding_get_data(f); }
|
||||
|
||||
static
|
||||
BinderCallForwardingCbData*
|
||||
binder_call_forwarding_callback_data_new(
|
||||
BinderCallback cb,
|
||||
void* data)
|
||||
{
|
||||
BinderCallForwardingCbData* cbd = g_slice_new0(BinderCallForwardingCbData);
|
||||
|
||||
cbd->cb.ptr = cb;
|
||||
cbd->data = data;
|
||||
return cbd;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_call_forwarding_callback_data_free(
|
||||
gpointer cbd)
|
||||
{
|
||||
g_slice_free(BinderCallForwardingCbData, cbd);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_call_forwarding_call(
|
||||
BinderCallForwarding* self,
|
||||
RADIO_REQ code,
|
||||
RADIO_CALL_FORWARD action,
|
||||
int reason,
|
||||
int cls,
|
||||
const struct ofono_phone_number* number,
|
||||
int time,
|
||||
RadioRequestCompleteFunc complete,
|
||||
BinderCallback cb,
|
||||
void* data)
|
||||
{
|
||||
/*
|
||||
* getCallForwardStatus(int32_t serial, CallForwardInfo callInfo);
|
||||
* setCallForward(int32_t serial, CallForwardInfo callInfo);
|
||||
*/
|
||||
GBinderWriter writer;
|
||||
RadioRequest* req = radio_request_new2(self->g, code, &writer, complete,
|
||||
binder_call_forwarding_callback_data_free,
|
||||
binder_call_forwarding_callback_data_new(cb, data));
|
||||
RadioCallForwardInfo* info = gbinder_writer_new0(&writer,
|
||||
RadioCallForwardInfo);
|
||||
guint parent;
|
||||
|
||||
info->status = action;
|
||||
info->reason = reason;
|
||||
info->serviceClass = cls;
|
||||
info->timeSeconds = time;
|
||||
if (number) {
|
||||
info->toa = number->type;
|
||||
binder_copy_hidl_string(&writer, &info->number, number->number);
|
||||
} else {
|
||||
info->toa = OFONO_NUMBER_TYPE_UNKNOWN;
|
||||
binder_copy_hidl_string(&writer, &info->number, NULL);
|
||||
}
|
||||
parent = gbinder_writer_append_buffer_object(&writer, info, sizeof(*info));
|
||||
binder_append_hidl_string_data(&writer, info, number, parent);
|
||||
|
||||
radio_request_submit(req);
|
||||
radio_request_unref(req);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_call_forwarding_set_cb(
|
||||
RadioRequest* req,
|
||||
RADIO_TX_STATUS status,
|
||||
RADIO_RESP resp,
|
||||
RADIO_ERROR error,
|
||||
const GBinderReader* args,
|
||||
void* user_data)
|
||||
{
|
||||
struct ofono_error err;
|
||||
const BinderCallForwardingCbData* cbd = user_data;
|
||||
ofono_call_forwarding_set_cb_t cb = cbd->cb.set;
|
||||
|
||||
if (status == RADIO_TX_STATUS_OK) {
|
||||
if (resp == RADIO_RESP_SET_CALL_FORWARD) {
|
||||
if (error == RADIO_ERROR_NONE) {
|
||||
cb(binder_error_ok(&err), cbd->data);
|
||||
return;
|
||||
} else {
|
||||
ofono_error("CF error %d", error);
|
||||
}
|
||||
} else {
|
||||
ofono_error("Unexpected setCallForward response %d", resp);
|
||||
}
|
||||
}
|
||||
cb(binder_error_failure(&err), cbd->data);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_call_forwarding_set(
|
||||
BinderCallForwarding* self,
|
||||
RADIO_CALL_FORWARD action,
|
||||
int reason,
|
||||
int cls,
|
||||
const struct ofono_phone_number* number,
|
||||
int time,
|
||||
ofono_call_forwarding_set_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
binder_call_forwarding_call(self, RADIO_REQ_SET_CALL_FORWARD,
|
||||
action, reason, cls, number, time, binder_call_forwarding_set_cb,
|
||||
BINDER_CB(cb), data);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_call_forwarding_registration(
|
||||
struct ofono_call_forwarding* f,
|
||||
int type,
|
||||
int cls,
|
||||
const struct ofono_phone_number* number,
|
||||
int time,
|
||||
ofono_call_forwarding_set_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
BinderCallForwarding* self = binder_call_forwarding_get_data(f);
|
||||
|
||||
DBG_(self, "%d", type);
|
||||
binder_call_forwarding_set(self, RADIO_CALL_FORWARD_REGISTRATION,
|
||||
type, cls, number, time, cb, data);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_call_forwarding_erasure(
|
||||
struct ofono_call_forwarding* f,
|
||||
int type,
|
||||
int cls,
|
||||
ofono_call_forwarding_set_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
BinderCallForwarding* self = binder_call_forwarding_get_data(f);
|
||||
|
||||
DBG_(self, "%d", type);
|
||||
binder_call_forwarding_set(self, RADIO_CALL_FORWARD_ERASURE,
|
||||
type, cls, NULL, CF_TIME_DEFAULT, cb, data);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_call_forwarding_deactivate(
|
||||
struct ofono_call_forwarding* f,
|
||||
int type,
|
||||
int cls,
|
||||
ofono_call_forwarding_set_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
BinderCallForwarding* self = binder_call_forwarding_get_data(f);
|
||||
|
||||
DBG_(self, "%d", type);
|
||||
binder_call_forwarding_set(self, RADIO_CALL_FORWARD_DISABLE,
|
||||
type, cls, NULL, CF_TIME_DEFAULT, cb, data);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_call_forwarding_activate(
|
||||
struct ofono_call_forwarding* f,
|
||||
int type,
|
||||
int cls,
|
||||
ofono_call_forwarding_set_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
BinderCallForwarding* self = binder_call_forwarding_get_data(f);
|
||||
|
||||
DBG_(self, "%d", type);
|
||||
binder_call_forwarding_set(self, RADIO_CALL_FORWARD_ENABLE,
|
||||
type, cls, NULL, CF_TIME_DEFAULT, cb, data);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_call_forwarding_query_ok(
|
||||
const BinderCallForwardingCbData* cbd,
|
||||
const GBinderReader* args)
|
||||
{
|
||||
struct ofono_error err;
|
||||
const RadioCallForwardInfo* infos;
|
||||
struct ofono_call_forwarding_condition* list = NULL;
|
||||
GBinderReader reader;
|
||||
gsize count = 0;
|
||||
|
||||
/* getCallForwardStatusResponse(RadioResponseInfo, vec<CallForwardInfo>) */
|
||||
gbinder_reader_copy(&reader, args);
|
||||
infos = gbinder_reader_read_hidl_type_vec(&reader, RadioCallForwardInfo,
|
||||
&count);
|
||||
if (count) {
|
||||
gsize i;
|
||||
|
||||
list = g_new0(struct ofono_call_forwarding_condition, count);
|
||||
for (i = 0; i < count; i++) {
|
||||
const RadioCallForwardInfo* info = infos + i;
|
||||
struct ofono_call_forwarding_condition* fw = list + i;
|
||||
|
||||
fw->status = info->status;
|
||||
fw->cls = info->serviceClass;
|
||||
fw->time = info->timeSeconds;
|
||||
fw->phone_number.type = info->toa;
|
||||
memcpy(fw->phone_number.number, info->number.data.str,
|
||||
MIN(OFONO_MAX_PHONE_NUMBER_LENGTH, info->number.len));
|
||||
}
|
||||
}
|
||||
cbd->cb.query(binder_error_ok(&err), count, list, cbd->data);
|
||||
g_free(list);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_call_forwarding_query_cb(
|
||||
RadioRequest* req,
|
||||
RADIO_TX_STATUS status,
|
||||
RADIO_RESP resp,
|
||||
RADIO_ERROR error,
|
||||
const GBinderReader* args,
|
||||
void* user_data)
|
||||
{
|
||||
struct ofono_error err;
|
||||
const BinderCallForwardingCbData* cbd = user_data;
|
||||
|
||||
if (status == RADIO_TX_STATUS_OK) {
|
||||
if (resp == RADIO_RESP_SET_CALL_FORWARD) {
|
||||
if (error == RADIO_ERROR_NONE) {
|
||||
binder_call_forwarding_query_ok(cbd, args);
|
||||
return;
|
||||
} else {
|
||||
ofono_error("CF query error %d", error);
|
||||
}
|
||||
} else {
|
||||
ofono_error("Unexpected getCallForwardStatus response %d", resp);
|
||||
}
|
||||
}
|
||||
cbd->cb.query(binder_error_failure(&err), 0, NULL, cbd->data);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_call_forwarding_query(
|
||||
struct ofono_call_forwarding* f,
|
||||
int type,
|
||||
int cls,
|
||||
ofono_call_forwarding_query_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
BinderCallForwarding* self = binder_call_forwarding_get_data(f);
|
||||
|
||||
DBG_(self, "%d", type);
|
||||
binder_call_forwarding_call(self, RADIO_REQ_GET_CALL_FORWARD_STATUS,
|
||||
RADIO_CALL_FORWARD_INTERROGATE, type, cls, NULL, CF_TIME_DEFAULT,
|
||||
binder_call_forwarding_query_cb, BINDER_CB(cb), data);
|
||||
}
|
||||
|
||||
static
|
||||
gboolean
|
||||
binder_call_forwarding_register(
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderCallForwarding* self = user_data;
|
||||
|
||||
GASSERT(self->register_id);
|
||||
self->register_id = 0;
|
||||
ofono_call_forwarding_register(self->f);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
binder_call_forwarding_probe(
|
||||
struct ofono_call_forwarding* f,
|
||||
unsigned int vendor,
|
||||
void* data)
|
||||
{
|
||||
BinderModem* modem = binder_modem_get_data(data);
|
||||
BinderCallForwarding* self = g_new0(BinderCallForwarding, 1);
|
||||
|
||||
self->f = f;
|
||||
self->g = radio_request_group_new(modem->client);
|
||||
self->log_prefix = binder_dup_prefix(modem->log_prefix);
|
||||
self->register_id = g_idle_add(binder_call_forwarding_register, self);
|
||||
|
||||
DBG_(self, "");
|
||||
ofono_call_forwarding_set_data(f, self);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_call_forwarding_remove(
|
||||
struct ofono_call_forwarding* f)
|
||||
{
|
||||
BinderCallForwarding* self = binder_call_forwarding_get_data(f);
|
||||
|
||||
DBG_(self, "");
|
||||
if (self->register_id) {
|
||||
g_source_remove(self->register_id);
|
||||
}
|
||||
radio_request_group_cancel(self->g);
|
||||
radio_request_group_unref(self->g);
|
||||
g_free(self->log_prefix);
|
||||
g_free(self);
|
||||
|
||||
ofono_call_forwarding_set_data(f, NULL);
|
||||
}
|
||||
|
||||
/*==========================================================================*
|
||||
* API
|
||||
*==========================================================================*/
|
||||
|
||||
static const struct ofono_call_forwarding_driver
|
||||
binder_call_forwarding_driver = {
|
||||
.name = BINDER_DRIVER,
|
||||
.probe = binder_call_forwarding_probe,
|
||||
.remove = binder_call_forwarding_remove,
|
||||
.erasure = binder_call_forwarding_erasure,
|
||||
.deactivation = binder_call_forwarding_deactivate,
|
||||
.query = binder_call_forwarding_query,
|
||||
.registration = binder_call_forwarding_registration,
|
||||
.activation = binder_call_forwarding_activate
|
||||
};
|
||||
|
||||
void
|
||||
binder_call_forwarding_init()
|
||||
{
|
||||
ofono_call_forwarding_driver_register(&binder_call_forwarding_driver);
|
||||
}
|
||||
|
||||
void
|
||||
binder_call_forwarding_cleanup()
|
||||
{
|
||||
ofono_call_forwarding_driver_unregister(&binder_call_forwarding_driver);
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
37
src/binder_call_forwarding.h
Normal file
37
src/binder_call_forwarding.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef BINDER_CALL_FORWARDING_H
|
||||
#define BINDER_CALL_FORWARDING_H
|
||||
|
||||
#include "binder_types.h"
|
||||
|
||||
void
|
||||
binder_call_forwarding_init(void)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_call_forwarding_cleanup(void)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
#endif /* BINDER_CALL_FORWARDING_H */
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
464
src/binder_call_settings.c
Normal file
464
src/binder_call_settings.c
Normal file
@@ -0,0 +1,464 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "binder_call_settings.h"
|
||||
#include "binder_log.h"
|
||||
#include "binder_modem.h"
|
||||
#include "binder_util.h"
|
||||
|
||||
#include <ofono/call-settings.h>
|
||||
#include <ofono/log.h>
|
||||
|
||||
#include <radio_request.h>
|
||||
#include <radio_request_group.h>
|
||||
|
||||
#include <gbinder_reader.h>
|
||||
#include <gbinder_writer.h>
|
||||
|
||||
typedef struct binder_call_settings {
|
||||
struct ofono_call_settings* s;
|
||||
RadioRequestGroup* g;
|
||||
char* log_prefix;
|
||||
guint register_id;
|
||||
} BinderCallSettings;
|
||||
|
||||
typedef struct binder_call_settings_cbd {
|
||||
BinderCallSettings* self;
|
||||
union call_settings_cb {
|
||||
ofono_call_settings_status_cb_t status;
|
||||
ofono_call_settings_set_cb_t set;
|
||||
ofono_call_settings_clir_cb_t clir;
|
||||
BinderCallback ptr;
|
||||
} cb;
|
||||
gpointer data;
|
||||
} BinderCallSettingsCbData;
|
||||
|
||||
#define DBG_(self,fmt,args...) DBG("%s" fmt, (self)->log_prefix, ##args)
|
||||
|
||||
static inline BinderCallSettings*
|
||||
binder_call_settings_get_data(struct ofono_call_settings* s)
|
||||
{ return ofono_call_settings_get_data(s); }
|
||||
|
||||
static
|
||||
BinderCallSettingsCbData*
|
||||
binder_call_settings_callback_data_new(
|
||||
BinderCallSettings* self,
|
||||
BinderCallback cb,
|
||||
void* data)
|
||||
{
|
||||
BinderCallSettingsCbData* cbd = g_slice_new0(BinderCallSettingsCbData);
|
||||
|
||||
cbd->self = self;
|
||||
cbd->cb.ptr = cb;
|
||||
cbd->data = data;
|
||||
return cbd;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_call_settings_callback_data_free(
|
||||
gpointer cbd)
|
||||
{
|
||||
g_slice_free(BinderCallSettingsCbData, cbd);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_call_settings_call(
|
||||
BinderCallSettings* self,
|
||||
RADIO_REQ code,
|
||||
RadioRequestCompleteFunc complete,
|
||||
BinderCallback cb,
|
||||
void* data)
|
||||
{
|
||||
RadioRequest* req = radio_request_new2(self->g, code, NULL, complete,
|
||||
binder_call_settings_callback_data_free,
|
||||
binder_call_settings_callback_data_new(self, cb, data));
|
||||
|
||||
radio_request_submit(req);
|
||||
radio_request_unref(req);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_call_settings_set_cb(
|
||||
RadioRequest* req,
|
||||
RADIO_TX_STATUS status,
|
||||
RADIO_RESP resp,
|
||||
RADIO_ERROR error,
|
||||
const GBinderReader* args,
|
||||
void* user_data)
|
||||
{
|
||||
struct ofono_error err;
|
||||
const BinderCallSettingsCbData* cbd = user_data;
|
||||
ofono_call_settings_set_cb_t cb = cbd->cb.set;
|
||||
|
||||
if (status == RADIO_TX_STATUS_OK && error == RADIO_ERROR_NONE) {
|
||||
cb(binder_error_ok(&err), cbd->data);
|
||||
} else {
|
||||
cb(binder_error_failure(&err), cbd->data);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_call_settings_cw_set(
|
||||
struct ofono_call_settings* s,
|
||||
int mode,
|
||||
int cls,
|
||||
ofono_call_settings_set_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
BinderCallSettings* self = binder_call_settings_get_data(s);
|
||||
|
||||
/* setCallWaiting(int32_t serial, bool enable, int32_t serviceClass); */
|
||||
GBinderWriter writer;
|
||||
RadioRequest* req = radio_request_new2(self->g,
|
||||
RADIO_REQ_SET_CALL_WAITING, &writer,
|
||||
binder_call_settings_set_cb,
|
||||
binder_call_settings_callback_data_free,
|
||||
binder_call_settings_callback_data_new(self, BINDER_CB(cb), data));
|
||||
|
||||
gbinder_writer_append_bool(&writer, mode); /* enable */
|
||||
gbinder_writer_append_int32(&writer, cls); /* serviceClass */
|
||||
|
||||
radio_request_submit(req);
|
||||
radio_request_unref(req);
|
||||
}
|
||||
|
||||
static
|
||||
gboolean
|
||||
binder_call_settings_cw_query_ok(
|
||||
const BinderCallSettingsCbData* cbd,
|
||||
const GBinderReader* args)
|
||||
{
|
||||
struct ofono_error err;
|
||||
ofono_call_settings_status_cb_t cb = cbd->cb.status;
|
||||
GBinderReader reader;
|
||||
gboolean enable;
|
||||
gint32 cls;
|
||||
|
||||
/*
|
||||
* getCallWaitingResponse(RadioResponseInfo, bool enable,
|
||||
* int32_t serviceClass);
|
||||
*/
|
||||
gbinder_reader_copy(&reader, args);
|
||||
if (gbinder_reader_read_bool(&reader, &enable) &&
|
||||
gbinder_reader_read_int32(&reader, &cls)) {
|
||||
if (enable) {
|
||||
DBG_(cbd->self, "CW enabled for %d", cls);
|
||||
cb(binder_error_ok(&err), cls, cbd->data);
|
||||
} else {
|
||||
DBG_(cbd->self, "CW disabled");
|
||||
cb(binder_error_ok(&err), 0, cbd->data);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
ofono_warn("Unexpected getCallWaitingResponse payload");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_call_settings_cw_query_cb(
|
||||
RadioRequest* req,
|
||||
RADIO_TX_STATUS status,
|
||||
RADIO_RESP resp,
|
||||
RADIO_ERROR error,
|
||||
const GBinderReader* args,
|
||||
void* user_data)
|
||||
{
|
||||
struct ofono_error err;
|
||||
const BinderCallSettingsCbData* cbd = user_data;
|
||||
|
||||
if (status == RADIO_TX_STATUS_OK) {
|
||||
if (resp == RADIO_RESP_GET_CALL_WAITING) {
|
||||
if (error == RADIO_ERROR_NONE) {
|
||||
if (binder_call_settings_cw_query_ok(cbd, args)) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
ofono_warn("CW query error %d", error);
|
||||
}
|
||||
} else {
|
||||
ofono_error("Unexpected getCallWaiting response %d", resp);
|
||||
}
|
||||
}
|
||||
cbd->cb.status(binder_error_failure(&err), -1, cbd->data);
|
||||
}
|
||||
|
||||
static
|
||||
void binder_call_settings_cw_query(
|
||||
struct ofono_call_settings* s,
|
||||
int cls,
|
||||
ofono_call_settings_status_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
BinderCallSettings* self = binder_call_settings_get_data(s);
|
||||
|
||||
/* getCallWaiting(int32_t serial, int32_t serviceClass); */
|
||||
GBinderWriter writer;
|
||||
RadioRequest* req = radio_request_new2(self->g,
|
||||
RADIO_REQ_GET_CALL_WAITING, &writer,
|
||||
binder_call_settings_cw_query_cb,
|
||||
binder_call_settings_callback_data_free,
|
||||
binder_call_settings_callback_data_new(self, BINDER_CB(cb), data));
|
||||
|
||||
gbinder_writer_append_int32(&writer, cls); /* serviceClass */
|
||||
|
||||
radio_request_submit(req);
|
||||
radio_request_unref(req);
|
||||
}
|
||||
|
||||
static
|
||||
gboolean
|
||||
binder_call_settings_clip_query_ok(
|
||||
const BinderCallSettingsCbData* cbd,
|
||||
const GBinderReader* args)
|
||||
{
|
||||
struct ofono_error err;
|
||||
GBinderReader reader;
|
||||
gint32 status;
|
||||
|
||||
/* getClipResponse(RadioResponseInfo, ClipStatus status); */
|
||||
gbinder_reader_copy(&reader, args);
|
||||
if (gbinder_reader_read_int32(&reader, &status)) {
|
||||
cbd->cb.status(binder_error_ok(&err), status, cbd->data);
|
||||
return TRUE;
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_call_settings_clip_query_cb(
|
||||
RadioRequest* req,
|
||||
RADIO_TX_STATUS status,
|
||||
RADIO_RESP resp,
|
||||
RADIO_ERROR error,
|
||||
const GBinderReader* args,
|
||||
void* user_data)
|
||||
{
|
||||
struct ofono_error err;
|
||||
const BinderCallSettingsCbData* cbd = user_data;
|
||||
|
||||
if (status == RADIO_TX_STATUS_OK) {
|
||||
if (resp == RADIO_RESP_GET_CLIP) {
|
||||
if (error == RADIO_ERROR_NONE) {
|
||||
if (binder_call_settings_clip_query_ok(cbd, args)) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
ofono_warn("CLIP query error %d", error);
|
||||
}
|
||||
} else {
|
||||
ofono_error("Unexpected getClip response %d", resp);
|
||||
}
|
||||
}
|
||||
cbd->cb.status(binder_error_failure(&err), -1, cbd->data);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_call_settings_clip_query(
|
||||
struct ofono_call_settings* s,
|
||||
ofono_call_settings_status_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
BinderCallSettings* self = binder_call_settings_get_data(s);
|
||||
|
||||
DBG_(self, "");
|
||||
/* getClip(int32_t serial); */
|
||||
binder_call_settings_call(self, RADIO_REQ_GET_CLIP,
|
||||
binder_call_settings_clip_query_cb, BINDER_CB(cb), data);
|
||||
}
|
||||
|
||||
static
|
||||
gboolean
|
||||
binder_call_settings_clir_ok(
|
||||
const BinderCallSettingsCbData* cbd,
|
||||
const GBinderReader* args)
|
||||
{
|
||||
struct ofono_error err;
|
||||
GBinderReader reader;
|
||||
gint32 n, m;
|
||||
|
||||
/* getClirResponse(RadioResponseInfo info, int32_t n, int32_t m); */
|
||||
gbinder_reader_copy(&reader, args);
|
||||
if (gbinder_reader_read_int32(&reader, &n) &&
|
||||
gbinder_reader_read_int32(&reader, &m)) {
|
||||
cbd->cb.clir(binder_error_ok(&err), n, m, cbd->data);
|
||||
return TRUE;
|
||||
}
|
||||
ofono_warn("Unexpected getClirResponse payload");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_call_settings_clir_cb(
|
||||
RadioRequest* req,
|
||||
RADIO_TX_STATUS status,
|
||||
RADIO_RESP resp,
|
||||
RADIO_ERROR error,
|
||||
const GBinderReader* args,
|
||||
void* user_data)
|
||||
{
|
||||
struct ofono_error err;
|
||||
const BinderCallSettingsCbData* cbd = user_data;
|
||||
|
||||
if (status == RADIO_TX_STATUS_OK) {
|
||||
if (resp == RADIO_RESP_GET_CLIR) {
|
||||
if (error == RADIO_ERROR_NONE) {
|
||||
if (binder_call_settings_clir_ok(cbd, args)) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
ofono_warn("CW query error %d", error);
|
||||
}
|
||||
} else {
|
||||
ofono_error("Unexpected getClir response %d", resp);
|
||||
}
|
||||
}
|
||||
cbd->cb.clir(binder_error_failure(&err), -1, -1, cbd->data);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_call_settings_clir_query(
|
||||
struct ofono_call_settings* s,
|
||||
ofono_call_settings_clir_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
BinderCallSettings* self = binder_call_settings_get_data(s);
|
||||
|
||||
DBG_(self, "");
|
||||
/* getClir(int32_t serial); */
|
||||
binder_call_settings_call(self, RADIO_REQ_GET_CLIR,
|
||||
binder_call_settings_clir_cb , BINDER_CB(cb), data);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_call_settings_clir_set(
|
||||
struct ofono_call_settings* s,
|
||||
int mode,
|
||||
ofono_call_settings_set_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
BinderCallSettings* self = binder_call_settings_get_data(s);
|
||||
|
||||
/* setClir(int32_t serial, int32_t status); */
|
||||
GBinderWriter writer;
|
||||
RadioRequest* req = radio_request_new2(self->g,
|
||||
RADIO_REQ_SET_CLIR, &writer,
|
||||
binder_call_settings_set_cb,
|
||||
binder_call_settings_callback_data_free,
|
||||
binder_call_settings_callback_data_new(self, BINDER_CB(cb), data));
|
||||
|
||||
DBG_(self, "%d", mode);
|
||||
gbinder_writer_append_int32(&writer, mode); /* status */
|
||||
|
||||
radio_request_submit(req);
|
||||
radio_request_unref(req);
|
||||
}
|
||||
|
||||
static
|
||||
gboolean
|
||||
binder_call_settings_register(
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderCallSettings* self = user_data;
|
||||
|
||||
DBG_(self, "");
|
||||
GASSERT(self->register_id);
|
||||
self->register_id = 0;
|
||||
ofono_call_settings_register(self->s);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
binder_call_settings_probe(
|
||||
struct ofono_call_settings* s,
|
||||
unsigned int vendor,
|
||||
void* data)
|
||||
{
|
||||
BinderModem* modem = binder_modem_get_data(data);
|
||||
BinderCallSettings* self = g_new0(BinderCallSettings, 1);
|
||||
|
||||
self->s = s;
|
||||
self->g = radio_request_group_new(modem->client);
|
||||
self->log_prefix = binder_dup_prefix(modem->log_prefix);
|
||||
self->register_id = g_idle_add(binder_call_settings_register, self);
|
||||
|
||||
DBG_(self, "");
|
||||
ofono_call_settings_set_data(s, self);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_call_settings_remove(
|
||||
struct ofono_call_settings* s)
|
||||
{
|
||||
BinderCallSettings* self = binder_call_settings_get_data(s);
|
||||
|
||||
DBG_(self, "");
|
||||
if (self->register_id) {
|
||||
g_source_remove(self->register_id);
|
||||
}
|
||||
radio_request_group_cancel(self->g);
|
||||
radio_request_group_unref(self->g);
|
||||
g_free(self->log_prefix);
|
||||
g_free(self);
|
||||
|
||||
ofono_call_settings_set_data(s, NULL);
|
||||
}
|
||||
|
||||
/*==========================================================================*
|
||||
* API
|
||||
*==========================================================================*/
|
||||
|
||||
static const struct ofono_call_settings_driver binder_call_settings_driver = {
|
||||
.name = BINDER_DRIVER,
|
||||
.probe = binder_call_settings_probe,
|
||||
.remove = binder_call_settings_remove,
|
||||
.clip_query = binder_call_settings_clip_query,
|
||||
.clir_query = binder_call_settings_clir_query,
|
||||
.clir_set = binder_call_settings_clir_set,
|
||||
.cw_query = binder_call_settings_cw_query,
|
||||
.cw_set = binder_call_settings_cw_set
|
||||
};
|
||||
|
||||
void
|
||||
binder_call_settings_init()
|
||||
{
|
||||
ofono_call_settings_driver_register(&binder_call_settings_driver);
|
||||
}
|
||||
|
||||
void
|
||||
binder_call_settings_cleanup()
|
||||
{
|
||||
ofono_call_settings_driver_unregister(&binder_call_settings_driver);
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
37
src/binder_call_settings.h
Normal file
37
src/binder_call_settings.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef BINDER_CALL_SETTINGS_H
|
||||
#define BINDER_CALL_SETTINGS_H
|
||||
|
||||
#include "binder_types.h"
|
||||
|
||||
void
|
||||
binder_call_settings_init(void)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_call_settings_cleanup(void)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
#endif /* BINDER_CALL_SETTINGS_H */
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
243
src/binder_call_volume.c
Normal file
243
src/binder_call_volume.c
Normal file
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "binder_call_volume.h"
|
||||
#include "binder_log.h"
|
||||
#include "binder_modem.h"
|
||||
#include "binder_util.h"
|
||||
|
||||
#include <ofono/call-volume.h>
|
||||
#include <ofono/log.h>
|
||||
|
||||
#include <radio_request.h>
|
||||
#include <radio_request_group.h>
|
||||
|
||||
#include <gbinder_reader.h>
|
||||
#include <gbinder_writer.h>
|
||||
|
||||
typedef struct binder_call_volume {
|
||||
struct ofono_call_volume* v;
|
||||
RadioRequestGroup* g;
|
||||
char* log_prefix;
|
||||
guint register_id;
|
||||
} BinderCallVolume;
|
||||
|
||||
typedef struct binder_call_volume_req {
|
||||
ofono_call_volume_cb_t cb;
|
||||
gpointer data;
|
||||
} BinderCallVolumeCbData;
|
||||
|
||||
#define DBG_(cd,fmt,args...) DBG("%s" fmt, (cd)->log_prefix, ##args)
|
||||
|
||||
static inline BinderCallVolume*
|
||||
binder_call_volume_get_data(struct ofono_call_volume* v)
|
||||
{ return ofono_call_volume_get_data(v); }
|
||||
|
||||
static
|
||||
BinderCallVolumeCbData*
|
||||
binder_call_volume_callback_data_new(
|
||||
ofono_call_volume_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
BinderCallVolumeCbData* cbd = g_slice_new0(BinderCallVolumeCbData);
|
||||
|
||||
cbd->cb = cb;
|
||||
cbd->data = data;
|
||||
return cbd;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_call_volume_callback_data_free(
|
||||
gpointer cbd)
|
||||
{
|
||||
g_slice_free(BinderCallVolumeCbData, cbd);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_call_volume_mute_cb(
|
||||
RadioRequest* req,
|
||||
RADIO_TX_STATUS status,
|
||||
RADIO_RESP resp,
|
||||
RADIO_ERROR error,
|
||||
const GBinderReader* args,
|
||||
void* user_data)
|
||||
{
|
||||
struct ofono_error err;
|
||||
const BinderCallVolumeCbData* cbd = user_data;
|
||||
ofono_call_volume_cb_t cb = cbd->cb;
|
||||
|
||||
if (status == RADIO_TX_STATUS_OK) {
|
||||
if (resp == RADIO_RESP_SET_MUTE) {
|
||||
if (error == RADIO_ERROR_NONE) {
|
||||
cb(binder_error_ok(&err), cbd->data);
|
||||
return;
|
||||
} else {
|
||||
ofono_warn("Could not set the mute state, error %d", error);
|
||||
}
|
||||
} else {
|
||||
ofono_error("Unexpected setMute response %d", resp);
|
||||
}
|
||||
}
|
||||
cb(binder_error_failure(&err), cbd->data);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_call_volume_mute(
|
||||
struct ofono_call_volume* v,
|
||||
int muted,
|
||||
ofono_call_volume_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
BinderCallVolume* self = binder_call_volume_get_data(v);
|
||||
|
||||
/* setMute(int32_t serial, bool enable); */
|
||||
GBinderWriter writer;
|
||||
RadioRequest* req = radio_request_new2(self->g,
|
||||
RADIO_REQ_SET_MUTE, &writer,
|
||||
binder_call_volume_mute_cb,
|
||||
binder_call_volume_callback_data_free,
|
||||
binder_call_volume_callback_data_new(cb, data));
|
||||
|
||||
DBG_(self, "%d", muted);
|
||||
gbinder_writer_append_bool(&writer, muted); /* enabled */
|
||||
|
||||
radio_request_submit(req);
|
||||
radio_request_unref(req);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_call_volume_query_mute_cb(
|
||||
RadioRequest* req,
|
||||
RADIO_TX_STATUS status,
|
||||
RADIO_RESP resp,
|
||||
RADIO_ERROR error,
|
||||
const GBinderReader* args,
|
||||
void* user_data)
|
||||
{
|
||||
if (status == RADIO_TX_STATUS_OK) {
|
||||
if (resp == RADIO_RESP_GET_MUTE) {
|
||||
if (error == RADIO_ERROR_NONE) {
|
||||
GBinderReader reader;
|
||||
gboolean muted;
|
||||
|
||||
/* getMuteResponse(RadioResponseInfo info, bool enable); */
|
||||
gbinder_reader_copy(&reader, args);
|
||||
if (gbinder_reader_read_bool(&reader, &muted)) {
|
||||
BinderCallVolume* self = user_data;
|
||||
|
||||
DBG_(self, "%d", muted);
|
||||
ofono_call_volume_set_muted(self->v, muted);
|
||||
}
|
||||
} else {
|
||||
ofono_warn("Could not get the mute state, error %d", error);
|
||||
}
|
||||
} else {
|
||||
ofono_error("Unexpected getMute response %d", resp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
gboolean
|
||||
binder_call_volume_register(
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderCallVolume* self = user_data;
|
||||
|
||||
DBG_(self, "");
|
||||
GASSERT(self->register_id);
|
||||
self->register_id = 0;
|
||||
ofono_call_volume_register(self->v);
|
||||
|
||||
/* Probe the mute state */
|
||||
binder_submit_request2(self->g, RADIO_REQ_GET_MUTE,
|
||||
binder_call_volume_query_mute_cb, NULL, self);
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
binder_call_volume_probe(
|
||||
struct ofono_call_volume* v,
|
||||
unsigned int vendor,
|
||||
void* data)
|
||||
{
|
||||
BinderModem* modem = binder_modem_get_data(data);
|
||||
BinderCallVolume* self = g_new0(BinderCallVolume, 1);
|
||||
|
||||
self->v = v;
|
||||
self->g = radio_request_group_new(modem->client);
|
||||
self->log_prefix = binder_dup_prefix(modem->log_prefix);
|
||||
self->register_id = g_idle_add(binder_call_volume_register, self);
|
||||
|
||||
DBG_(self, "");
|
||||
ofono_call_volume_set_data(v, self);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_call_volume_remove(
|
||||
struct ofono_call_volume* v)
|
||||
{
|
||||
BinderCallVolume* self = binder_call_volume_get_data(v);
|
||||
|
||||
DBG_(self, "");
|
||||
if (self->register_id) {
|
||||
g_source_remove(self->register_id);
|
||||
}
|
||||
radio_request_group_cancel(self->g);
|
||||
radio_request_group_unref(self->g);
|
||||
g_free(self->log_prefix);
|
||||
g_free(self);
|
||||
|
||||
ofono_call_volume_set_data(v, NULL);
|
||||
}
|
||||
|
||||
/*==========================================================================*
|
||||
* API
|
||||
*==========================================================================*/
|
||||
|
||||
static const struct ofono_call_volume_driver binder_call_volume_driver = {
|
||||
.name = BINDER_DRIVER,
|
||||
.probe = binder_call_volume_probe,
|
||||
.remove = binder_call_volume_remove,
|
||||
.mute = binder_call_volume_mute
|
||||
};
|
||||
|
||||
void
|
||||
binder_call_volume_init()
|
||||
{
|
||||
ofono_call_volume_driver_register(&binder_call_volume_driver);
|
||||
}
|
||||
|
||||
void
|
||||
binder_call_volume_cleanup()
|
||||
{
|
||||
ofono_call_volume_driver_unregister(&binder_call_volume_driver);
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
37
src/binder_call_volume.h
Normal file
37
src/binder_call_volume.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef BINDER_CALL_VOLUME_H
|
||||
#define BINDER_CALL_VOLUME_H
|
||||
|
||||
#include "binder_types.h"
|
||||
|
||||
void
|
||||
binder_call_volume_init(void)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_call_volume_cleanup(void)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
#endif /* BINDER_CALL_VOLUME_H */
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
388
src/binder_cbs.c
Normal file
388
src/binder_cbs.c
Normal file
@@ -0,0 +1,388 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "binder_cbs.h"
|
||||
#include "binder_log.h"
|
||||
#include "binder_modem.h"
|
||||
#include "binder_util.h"
|
||||
|
||||
#include <ofono/cbs.h>
|
||||
#include <ofono/log.h>
|
||||
|
||||
#include <radio_client.h>
|
||||
#include <radio_request.h>
|
||||
#include <radio_request_group.h>
|
||||
|
||||
#include <gbinder_reader.h>
|
||||
#include <gbinder_writer.h>
|
||||
|
||||
#include <gutil_macros.h>
|
||||
#include <gutil_strv.h>
|
||||
|
||||
typedef struct binder_cbs {
|
||||
struct ofono_cbs* cbs;
|
||||
RadioRequestGroup* g;
|
||||
char* log_prefix;
|
||||
guint register_id;
|
||||
gulong event_id;
|
||||
} BinderCbs;
|
||||
|
||||
typedef struct binder_cbs_cbd {
|
||||
BinderCbs* self;
|
||||
ofono_cbs_set_cb_t cb;
|
||||
gpointer data;
|
||||
} BinderCbsCbData;
|
||||
|
||||
#define CBS_CHECK_RETRY_MS 1000
|
||||
#define CBS_CHECK_RETRY_COUNT 30
|
||||
|
||||
#define DBG_(cd,fmt,args...) DBG("%s" fmt, (cd)->log_prefix, ##args)
|
||||
|
||||
static inline BinderCbs* binder_cbs_get_data(struct ofono_cbs* cbs)
|
||||
{ return ofono_cbs_get_data(cbs); }
|
||||
|
||||
static
|
||||
BinderCbsCbData*
|
||||
binder_cbs_callback_data_new(
|
||||
BinderCbs* self,
|
||||
ofono_cbs_set_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
BinderCbsCbData* cbd = g_slice_new0(BinderCbsCbData);
|
||||
|
||||
cbd->self = self;
|
||||
cbd->cb = cb;
|
||||
cbd->data = data;
|
||||
return cbd;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_cbs_callback_data_free(
|
||||
gpointer cbd)
|
||||
{
|
||||
g_slice_free(BinderCbsCbData, cbd);
|
||||
}
|
||||
|
||||
static
|
||||
gboolean
|
||||
binder_cbs_retry(
|
||||
RadioRequest* req,
|
||||
RADIO_TX_STATUS status,
|
||||
RADIO_RESP resp,
|
||||
RADIO_ERROR error,
|
||||
const GBinderReader* args,
|
||||
void* user_data)
|
||||
{
|
||||
return error == RADIO_ERROR_INVALID_STATE;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_cbs_activate_cb(
|
||||
RadioRequest* req,
|
||||
RADIO_TX_STATUS status,
|
||||
RADIO_RESP resp,
|
||||
RADIO_ERROR error,
|
||||
const GBinderReader* args,
|
||||
gpointer user_data)
|
||||
{
|
||||
struct ofono_error err;
|
||||
BinderCbsCbData* cbd = user_data;
|
||||
|
||||
if (status == RADIO_TX_STATUS_OK) {
|
||||
if (resp == RADIO_RESP_SET_GSM_BROADCAST_ACTIVATION) {
|
||||
if (error == RADIO_ERROR_NONE) {
|
||||
cbd->cb(binder_error_ok(&err), cbd->data);
|
||||
return;
|
||||
} else {
|
||||
ofono_warn("Failed to configure broadcasts, error %s",
|
||||
binder_radio_error_string(error));
|
||||
}
|
||||
} else {
|
||||
ofono_error("Unexpected setGsmBroadcastActivation response %d",
|
||||
resp);
|
||||
}
|
||||
}
|
||||
cbd->cb(binder_error_failure(&err), cbd->data);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_cbs_activate(
|
||||
BinderCbs* self,
|
||||
gboolean activate,
|
||||
ofono_cbs_set_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
/* setGsmBroadcastActivation(int32_t serial, bool activate); */
|
||||
GBinderWriter writer;
|
||||
RadioRequest* req = radio_request_new2(self->g,
|
||||
RADIO_REQ_SET_GSM_BROADCAST_ACTIVATION, &writer,
|
||||
binder_cbs_activate_cb,
|
||||
binder_cbs_callback_data_free,
|
||||
binder_cbs_callback_data_new(self, cb, data));
|
||||
|
||||
gbinder_writer_append_bool(&writer, activate); /* activate */
|
||||
DBG_(self, "%sactivating CB", activate ? "" : "de");
|
||||
radio_request_set_retry_func(req, binder_cbs_retry);
|
||||
radio_request_set_retry(req, CBS_CHECK_RETRY_MS, CBS_CHECK_RETRY_COUNT);
|
||||
radio_request_submit(req);
|
||||
radio_request_unref(req);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_cbs_set_config_cb(
|
||||
RadioRequest* req,
|
||||
RADIO_TX_STATUS status,
|
||||
RADIO_RESP resp,
|
||||
RADIO_ERROR error,
|
||||
const GBinderReader* args,
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderCbsCbData* cbd = user_data;
|
||||
struct ofono_error err;
|
||||
|
||||
if (status == RADIO_TX_STATUS_OK) {
|
||||
if (resp == RADIO_RESP_SET_GSM_BROADCAST_CONFIG) {
|
||||
if (error == RADIO_ERROR_NONE) {
|
||||
binder_cbs_activate(cbd->self, TRUE, cbd->cb, cbd->data);
|
||||
return;
|
||||
} else {
|
||||
ofono_warn("Failed to set broadcast config, error %d", error);
|
||||
}
|
||||
} else {
|
||||
ofono_error("Unexpected setGsmBroadcastConfig response %d",
|
||||
resp);
|
||||
}
|
||||
}
|
||||
cbd->cb(binder_error_failure(&err), cbd->data);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_cbs_set_config(
|
||||
BinderCbs* self,
|
||||
const char* topics,
|
||||
ofono_cbs_set_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
/* setGsmBroadcastConfig(int32_t serial, vec<GsmBroadcastSmsConfigInfo>); */
|
||||
GBinderWriter writer;
|
||||
RadioRequest* req = radio_request_new2(self->g,
|
||||
RADIO_REQ_SET_GSM_BROADCAST_CONFIG, &writer,
|
||||
binder_cbs_set_config_cb,
|
||||
binder_cbs_callback_data_free,
|
||||
binder_cbs_callback_data_new(self, cb, data));
|
||||
|
||||
GBinderParent parent;
|
||||
GBinderHidlVec* vec = gbinder_writer_new0(&writer, GBinderHidlVec);
|
||||
RadioGsmBroadcastSmsConfig* configs = NULL;
|
||||
char** list = topics ? g_strsplit(topics, ",", 0) : NULL;
|
||||
const guint count = gutil_strv_length(list);
|
||||
guint i;
|
||||
|
||||
vec->count = count;
|
||||
vec->owns_buffer = TRUE;
|
||||
vec->data.ptr = configs = gbinder_writer_malloc0(&writer,
|
||||
sizeof(RadioGsmBroadcastSmsConfig) * count);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
RadioGsmBroadcastSmsConfig* config = configs + i;
|
||||
const char* entry = list[i];
|
||||
const char* delim = strchr(entry, '-');
|
||||
|
||||
config->selected = TRUE;
|
||||
config->toCodeScheme = 0xff;
|
||||
if (delim) {
|
||||
char** range = g_strsplit(entry, "-", 0);
|
||||
|
||||
config->fromServiceId = atoi(range[0]);
|
||||
config->toServiceId = atoi(range[1]);
|
||||
g_strfreev(range);
|
||||
} else {
|
||||
config->fromServiceId = config->toServiceId = atoi(entry);
|
||||
}
|
||||
}
|
||||
|
||||
/* Every vector, even the one without data, requires two buffer objects */
|
||||
parent.offset = GBINDER_HIDL_VEC_BUFFER_OFFSET;
|
||||
parent.index = gbinder_writer_append_buffer_object(&writer, vec,
|
||||
sizeof(*vec));
|
||||
gbinder_writer_append_buffer_object_with_parent(&writer, configs,
|
||||
sizeof(configs[0]) * count, &parent);
|
||||
|
||||
DBG_(self, "configuring CB");
|
||||
radio_request_set_retry_func(req, binder_cbs_retry);
|
||||
radio_request_set_retry(req, CBS_CHECK_RETRY_MS, CBS_CHECK_RETRY_COUNT);
|
||||
radio_request_submit(req);
|
||||
radio_request_unref(req);
|
||||
g_strfreev(list);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_cbs_set_topics(
|
||||
struct ofono_cbs* cbs,
|
||||
const char* topics,
|
||||
ofono_cbs_set_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
BinderCbs* self = binder_cbs_get_data(cbs);
|
||||
|
||||
DBG_(self, "%s", topics);
|
||||
binder_cbs_set_config(self, topics, cb, data);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_cbs_clear_topics(
|
||||
struct ofono_cbs* cbs,
|
||||
ofono_cbs_set_cb_t cb,
|
||||
void *data)
|
||||
{
|
||||
BinderCbs* self = binder_cbs_get_data(cbs);
|
||||
|
||||
DBG_(self, "");
|
||||
binder_cbs_activate(self, FALSE, cb, data);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_cbs_notify(
|
||||
RadioClient* client,
|
||||
RADIO_IND code,
|
||||
const GBinderReader* args,
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderCbs* self = user_data;
|
||||
GBinderReader reader;
|
||||
const guchar* ptr;
|
||||
gsize len;
|
||||
|
||||
/* newBroadcastSms(RadioIndicationType type, vec<uint8_t> data); */
|
||||
GASSERT(code == RADIO_IND_NEW_BROADCAST_SMS);
|
||||
gbinder_reader_copy(&reader, args);
|
||||
ptr = gbinder_reader_read_hidl_byte_vec(&reader, &len);
|
||||
|
||||
/* By default assume that it's a length followed by the binary PDU data. */
|
||||
if (ptr) {
|
||||
if (len > 4) {
|
||||
const guint32 pdu_len = GUINT32_FROM_LE(*(guint32*)ptr);
|
||||
|
||||
if (G_ALIGN4(pdu_len) == (len - 4)) {
|
||||
DBG_(self, "%u bytes", pdu_len);
|
||||
ofono_cbs_notify(self->cbs, ptr + 4, pdu_len);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* But I've seen cell broadcasts arriving without the length,
|
||||
* simply as a blob.
|
||||
*/
|
||||
ofono_cbs_notify(self->cbs, ptr, (guint) len);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
gboolean
|
||||
binder_cbs_register(
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderCbs* self = user_data;
|
||||
RadioClient* client = self->g->client;
|
||||
|
||||
GASSERT(self->register_id);
|
||||
self->register_id = 0;
|
||||
DBG_(self, "registering for CB");
|
||||
self->event_id = radio_client_add_indication_handler(client,
|
||||
RADIO_IND_NEW_BROADCAST_SMS, binder_cbs_notify, self);
|
||||
ofono_cbs_register(self->cbs);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
binder_cbs_probe(
|
||||
struct ofono_cbs* cbs,
|
||||
unsigned int vendor,
|
||||
void* data)
|
||||
{
|
||||
BinderModem* modem = binder_modem_get_data(data);
|
||||
BinderCbs* self = g_new0(BinderCbs, 1);
|
||||
|
||||
self->cbs = cbs;
|
||||
self->g = radio_request_group_new(modem->client); /* Keeps ref to client */
|
||||
self->log_prefix = binder_dup_prefix(modem->log_prefix);
|
||||
self->register_id = g_idle_add(binder_cbs_register, self);
|
||||
|
||||
DBG_(self, "");
|
||||
ofono_cbs_set_data(cbs, self);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_cbs_remove(
|
||||
struct ofono_cbs* cbs)
|
||||
{
|
||||
BinderCbs* self = binder_cbs_get_data(cbs);
|
||||
|
||||
DBG_(self, "");
|
||||
if (self->register_id) {
|
||||
g_source_remove(self->register_id);
|
||||
}
|
||||
radio_client_remove_handler(self->g->client, self->event_id);
|
||||
radio_request_group_cancel(self->g);
|
||||
radio_request_group_unref(self->g);
|
||||
g_free(self->log_prefix);
|
||||
g_free(self);
|
||||
|
||||
ofono_cbs_set_data(cbs, NULL);
|
||||
}
|
||||
|
||||
/*==========================================================================*
|
||||
* API
|
||||
*==========================================================================*/
|
||||
|
||||
static const struct ofono_cbs_driver binder_cbs_driver = {
|
||||
.name = BINDER_DRIVER,
|
||||
.probe = binder_cbs_probe,
|
||||
.remove = binder_cbs_remove,
|
||||
.set_topics = binder_cbs_set_topics,
|
||||
.clear_topics = binder_cbs_clear_topics
|
||||
};
|
||||
|
||||
void
|
||||
binder_cbs_init()
|
||||
{
|
||||
ofono_cbs_driver_register(&binder_cbs_driver);
|
||||
}
|
||||
|
||||
void
|
||||
binder_cbs_cleanup()
|
||||
{
|
||||
ofono_cbs_driver_unregister(&binder_cbs_driver);
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
37
src/binder_cbs.h
Normal file
37
src/binder_cbs.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef BINDER_CBS_H
|
||||
#define BINDER_CBS_H
|
||||
|
||||
#include "binder_types.h"
|
||||
|
||||
void
|
||||
binder_cbs_init(void)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_cbs_cleanup(void)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
#endif /* BINDER_CBS_H */
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
939
src/binder_cell_info.c
Normal file
939
src/binder_cell_info.c
Normal file
@@ -0,0 +1,939 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "binder_cell_info.h"
|
||||
#include "binder_sim_card.h"
|
||||
#include "binder_radio.h"
|
||||
#include "binder_util.h"
|
||||
#include "binder_log.h"
|
||||
|
||||
#include <radio_client.h>
|
||||
#include <radio_request.h>
|
||||
#include <radio_util.h>
|
||||
|
||||
#include <gbinder_reader.h>
|
||||
#include <gbinder_writer.h>
|
||||
|
||||
#include <gutil_idlepool.h>
|
||||
#include <gutil_macros.h>
|
||||
#include <gutil_misc.h>
|
||||
|
||||
#define DEFAULT_UPDATE_RATE_MS (10000) /* 10 sec */
|
||||
#define MAX_RETRIES (5)
|
||||
|
||||
enum binder_cell_info_event {
|
||||
CELL_INFO_EVENT_1_0,
|
||||
CELL_INFO_EVENT_1_2,
|
||||
CELL_INFO_EVENT_1_4,
|
||||
CELL_INFO_EVENT_COUNT
|
||||
};
|
||||
|
||||
typedef GObjectClass BinderCellInfoClass;
|
||||
typedef struct binder_cell_info {
|
||||
GObject object;
|
||||
struct ofono_cell_info info;
|
||||
struct ofono_cell **cells;
|
||||
RadioClient* client;
|
||||
BinderRadio* radio;
|
||||
BinderSimCard* sim_card;
|
||||
gulong radio_state_event_id;
|
||||
gulong sim_status_event_id;
|
||||
gboolean sim_card_ready;
|
||||
int update_rate_ms;
|
||||
char* log_prefix;
|
||||
gulong event_id[CELL_INFO_EVENT_COUNT];
|
||||
RadioRequest* query_req;
|
||||
RadioRequest* set_rate_req;
|
||||
gboolean enabled;
|
||||
} BinderCellInfo;
|
||||
|
||||
enum binder_cell_info_signal {
|
||||
SIGNAL_CELLS_CHANGED,
|
||||
SIGNAL_COUNT
|
||||
};
|
||||
|
||||
#define SIGNAL_CELLS_CHANGED_NAME "binder-cell-info-cells-changed"
|
||||
|
||||
static guint binder_cell_info_signals[SIGNAL_COUNT] = { 0 };
|
||||
|
||||
G_DEFINE_TYPE(BinderCellInfo, binder_cell_info, G_TYPE_OBJECT)
|
||||
#define THIS_TYPE (binder_cell_info_get_type())
|
||||
#define THIS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), THIS_TYPE, BinderCellInfo))
|
||||
#define PARENT_CLASS binder_cell_info_parent_class
|
||||
|
||||
#define DBG_(self,fmt,args...) DBG("%s" fmt, (self)->log_prefix, ##args)
|
||||
|
||||
/*
|
||||
* binder_cell_info_list_equal() assumes that zero-initialized
|
||||
* struct ofono_cell gets allocated regardless of the cell type,
|
||||
* even if a part of the structure remains unused.
|
||||
*/
|
||||
|
||||
#define binder_cell_new() g_new0(struct ofono_cell, 1)
|
||||
|
||||
static
|
||||
const char*
|
||||
binder_cell_info_int_format(
|
||||
int value,
|
||||
const char* format)
|
||||
{
|
||||
if (value == OFONO_CELL_INVALID_VALUE) {
|
||||
return "";
|
||||
} else {
|
||||
static GUtilIdlePool* binder_cell_info_pool = NULL;
|
||||
GUtilIdlePool* pool = gutil_idle_pool_get(&binder_cell_info_pool);
|
||||
char* str = g_strdup_printf(format, value);
|
||||
|
||||
gutil_idle_pool_add(pool, str, g_free);
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
gint
|
||||
binder_cell_info_list_compare(
|
||||
gconstpointer a,
|
||||
gconstpointer b)
|
||||
{
|
||||
return ofono_cell_compare_location(*(struct ofono_cell**)a,
|
||||
*(struct ofono_cell**)b);
|
||||
}
|
||||
|
||||
static
|
||||
gboolean
|
||||
binder_cell_info_list_equal(
|
||||
const ofono_cell_ptr* l1,
|
||||
const ofono_cell_ptr* l2)
|
||||
{
|
||||
if (l1 && l2) {
|
||||
while (*l1 && *l2) {
|
||||
if (memcmp(*l1, *l2, sizeof(struct ofono_cell))) {
|
||||
return FALSE;
|
||||
}
|
||||
l1++;
|
||||
l2++;
|
||||
}
|
||||
return !*l1 && !*l2;
|
||||
} else {
|
||||
return (!l1 || !*l1) && (!l2 || !*l2);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_cell_info_clear(
|
||||
BinderCellInfo* self)
|
||||
{
|
||||
if (self->cells && self->cells[0]) {
|
||||
gutil_ptrv_free((void**)self->cells);
|
||||
self->info.cells = self->cells = g_new0(struct ofono_cell*, 1);
|
||||
g_signal_emit(self, binder_cell_info_signals[SIGNAL_CELLS_CHANGED], 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* NULL-terminates and takes ownership of GPtrArray */
|
||||
static
|
||||
void
|
||||
binder_cell_info_update_cells(
|
||||
BinderCellInfo* self,
|
||||
GPtrArray* l)
|
||||
{
|
||||
if (l) {
|
||||
g_ptr_array_sort(l, binder_cell_info_list_compare);
|
||||
g_ptr_array_add(l, NULL);
|
||||
|
||||
DBG_(self, "%d cell(s)", (int)(l->len - 1));
|
||||
if (!binder_cell_info_list_equal(self->cells,
|
||||
(struct ofono_cell**)l->pdata)) {
|
||||
gutil_ptrv_free((void**)self->cells);
|
||||
self->info.cells = self->cells = (struct ofono_cell **)
|
||||
g_ptr_array_free(l, FALSE);
|
||||
g_signal_emit(self, binder_cell_info_signals
|
||||
[SIGNAL_CELLS_CHANGED], 0);
|
||||
} else {
|
||||
g_ptr_array_set_free_func(l, g_free);
|
||||
g_ptr_array_free(l, TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_cell_info_invalidate(
|
||||
void* info,
|
||||
gsize size)
|
||||
{
|
||||
const int n = size/sizeof(int);
|
||||
int* value = info;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
*value++ = OFONO_CELL_INVALID_VALUE;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
struct ofono_cell*
|
||||
binder_cell_info_new_cell_gsm(
|
||||
gboolean registered,
|
||||
const RadioCellIdentityGsm* id,
|
||||
const RadioSignalStrengthGsm* ss)
|
||||
{
|
||||
struct ofono_cell* cell = binder_cell_new();
|
||||
struct ofono_cell_info_gsm* gsm = &cell->info.gsm;
|
||||
|
||||
cell->type = OFONO_CELL_TYPE_GSM;
|
||||
cell->registered = registered;
|
||||
|
||||
binder_cell_info_invalidate(gsm, sizeof(*gsm));
|
||||
gutil_parse_int(id->mcc.data.str, 10, &gsm->mcc);
|
||||
gutil_parse_int(id->mnc.data.str, 10, &gsm->mnc);
|
||||
gsm->lac = id->lac;
|
||||
gsm->cid = id->cid;
|
||||
gsm->arfcn = id->arfcn;
|
||||
gsm->bsic = id->bsic;
|
||||
gsm->signalStrength = ss->signalStrength;
|
||||
gsm->bitErrorRate = ss->bitErrorRate;
|
||||
gsm->timingAdvance = ss->timingAdvance;
|
||||
DBG("[gsm] reg=%d%s%s%s%s%s%s%s%s%s", registered,
|
||||
binder_cell_info_int_format(gsm->mcc, ",mcc=%d"),
|
||||
binder_cell_info_int_format(gsm->mnc, ",mnc=%d"),
|
||||
binder_cell_info_int_format(gsm->lac, ",lac=%d"),
|
||||
binder_cell_info_int_format(gsm->cid, ",cid=%d"),
|
||||
binder_cell_info_int_format(gsm->arfcn, ",arfcn=%d"),
|
||||
binder_cell_info_int_format(gsm->bsic, ",bsic=%d"),
|
||||
binder_cell_info_int_format(gsm->signalStrength, ",strength=%d"),
|
||||
binder_cell_info_int_format(gsm->bitErrorRate, ",err=%d"),
|
||||
binder_cell_info_int_format(gsm->timingAdvance, ",t=%d"));
|
||||
return cell;
|
||||
}
|
||||
|
||||
static
|
||||
struct
|
||||
ofono_cell*
|
||||
binder_cell_info_new_cell_wcdma(
|
||||
gboolean registered,
|
||||
const RadioCellIdentityWcdma* id,
|
||||
const RadioSignalStrengthWcdma* ss)
|
||||
{
|
||||
struct ofono_cell* cell = binder_cell_new();
|
||||
struct ofono_cell_info_wcdma* wcdma = &cell->info.wcdma;
|
||||
|
||||
cell->type = OFONO_CELL_TYPE_WCDMA;
|
||||
cell->registered = registered;
|
||||
|
||||
binder_cell_info_invalidate(wcdma, sizeof(*wcdma));
|
||||
gutil_parse_int(id->mcc.data.str, 10, &wcdma->mcc);
|
||||
gutil_parse_int(id->mnc.data.str, 10, &wcdma->mnc);
|
||||
wcdma->lac = id->lac;
|
||||
wcdma->cid = id->cid;
|
||||
wcdma->psc = id->psc;
|
||||
wcdma->uarfcn = id->uarfcn;
|
||||
wcdma->signalStrength = ss->signalStrength;
|
||||
wcdma->bitErrorRate = ss->bitErrorRate;
|
||||
DBG("[wcdma] reg=%d%s%s%s%s%s%s%s", registered,
|
||||
binder_cell_info_int_format(wcdma->mcc, ",mcc=%d"),
|
||||
binder_cell_info_int_format(wcdma->mnc, ",mnc=%d"),
|
||||
binder_cell_info_int_format(wcdma->lac, ",lac=%d"),
|
||||
binder_cell_info_int_format(wcdma->cid, ",cid=%d"),
|
||||
binder_cell_info_int_format(wcdma->psc, ",psc=%d"),
|
||||
binder_cell_info_int_format(wcdma->signalStrength, ",strength=%d"),
|
||||
binder_cell_info_int_format(wcdma->bitErrorRate, ",err=%d"));
|
||||
return cell;
|
||||
}
|
||||
|
||||
static
|
||||
struct ofono_cell*
|
||||
binder_cell_info_new_cell_lte(
|
||||
gboolean registered,
|
||||
const RadioCellIdentityLte* id,
|
||||
const RadioSignalStrengthLte* ss)
|
||||
{
|
||||
struct ofono_cell* cell = binder_cell_new();
|
||||
struct ofono_cell_info_lte* lte = &cell->info.lte;
|
||||
|
||||
cell->type = OFONO_CELL_TYPE_LTE;
|
||||
cell->registered = registered;
|
||||
|
||||
binder_cell_info_invalidate(lte, sizeof(*lte));
|
||||
gutil_parse_int(id->mcc.data.str, 10, <e->mcc);
|
||||
gutil_parse_int(id->mnc.data.str, 10, <e->mnc);
|
||||
lte->ci = id->ci;
|
||||
lte->pci = id->pci;
|
||||
lte->tac = id->tac;
|
||||
lte->earfcn = id->earfcn;
|
||||
lte->signalStrength = ss->signalStrength;
|
||||
lte->rsrp = ss->rsrp;
|
||||
lte->rsrq = ss->rsrq;
|
||||
lte->rssnr = ss->rssnr;
|
||||
lte->cqi = ss->cqi;
|
||||
lte->timingAdvance = ss->timingAdvance;
|
||||
DBG("[lte] reg=%d%s%s%s%s%s%s%s%s%s%s%s", registered,
|
||||
binder_cell_info_int_format(lte->mcc, ",mcc=%d"),
|
||||
binder_cell_info_int_format(lte->mnc, ",mnc=%d"),
|
||||
binder_cell_info_int_format(lte->ci, ",ci=%d"),
|
||||
binder_cell_info_int_format(lte->pci, ",pci=%d"),
|
||||
binder_cell_info_int_format(lte->tac, ",tac=%d"),
|
||||
binder_cell_info_int_format(lte->signalStrength, ",strength=%d"),
|
||||
binder_cell_info_int_format(lte->rsrp, ",rsrp=%d"),
|
||||
binder_cell_info_int_format(lte->rsrq, ",rsrq=%d"),
|
||||
binder_cell_info_int_format(lte->rssnr, ",rssnr=%d"),
|
||||
binder_cell_info_int_format(lte->cqi, ",cqi=%d"),
|
||||
binder_cell_info_int_format(lte->timingAdvance, ",t=%d"));
|
||||
return cell;
|
||||
}
|
||||
|
||||
static
|
||||
GPtrArray*
|
||||
binder_cell_info_array_new_1_0(
|
||||
const RadioCellInfo* cells,
|
||||
gsize count)
|
||||
{
|
||||
gsize i;
|
||||
GPtrArray* l = g_ptr_array_sized_new(count + 1);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
const RadioCellInfo* cell = cells + i;
|
||||
const gboolean reg = cell->registered;
|
||||
const RadioCellInfoGsm* gsm;
|
||||
const RadioCellInfoLte* lte;
|
||||
const RadioCellInfoWcdma* wcdma;
|
||||
guint j;
|
||||
|
||||
switch (cell->cellInfoType) {
|
||||
case RADIO_CELL_INFO_GSM:
|
||||
gsm = cell->gsm.data.ptr;
|
||||
for (j = 0; j < cell->gsm.count; j++) {
|
||||
g_ptr_array_add(l, binder_cell_info_new_cell_gsm(reg,
|
||||
&gsm[j].cellIdentityGsm,
|
||||
&gsm[j].signalStrengthGsm));
|
||||
}
|
||||
continue;
|
||||
case RADIO_CELL_INFO_LTE:
|
||||
lte = cell->lte.data.ptr;
|
||||
for (j = 0; j < cell->lte.count; j++) {
|
||||
g_ptr_array_add(l, binder_cell_info_new_cell_lte(reg,
|
||||
<e[j].cellIdentityLte,
|
||||
<e[j].signalStrengthLte));
|
||||
}
|
||||
continue;
|
||||
case RADIO_CELL_INFO_WCDMA:
|
||||
wcdma = cell->wcdma.data.ptr;
|
||||
for (j = 0; j < cell->wcdma.count; j++) {
|
||||
g_ptr_array_add(l, binder_cell_info_new_cell_wcdma(reg,
|
||||
&wcdma[j].cellIdentityWcdma,
|
||||
&wcdma[j].signalStrengthWcdma));
|
||||
}
|
||||
continue;
|
||||
case RADIO_CELL_INFO_CDMA:
|
||||
case RADIO_CELL_INFO_TD_SCDMA:
|
||||
break;
|
||||
}
|
||||
DBG("unsupported cell type %d", cell->cellInfoType);
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
static
|
||||
GPtrArray*
|
||||
binder_cell_info_array_new_1_2(
|
||||
const RadioCellInfo_1_2* cells,
|
||||
gsize count)
|
||||
{
|
||||
gsize i;
|
||||
GPtrArray* l = g_ptr_array_sized_new(count + 1);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
const RadioCellInfo_1_2* cell = cells + i;
|
||||
const gboolean registered = cell->registered;
|
||||
const RadioCellInfoGsm_1_2* gsm;
|
||||
const RadioCellInfoLte_1_2* lte;
|
||||
const RadioCellInfoWcdma_1_2* wcdma;
|
||||
guint j;
|
||||
|
||||
switch (cell->cellInfoType) {
|
||||
case RADIO_CELL_INFO_GSM:
|
||||
gsm = cell->gsm.data.ptr;
|
||||
for (j = 0; j < cell->gsm.count; j++) {
|
||||
g_ptr_array_add(l, binder_cell_info_new_cell_gsm(registered,
|
||||
&gsm[j].cellIdentityGsm.base,
|
||||
&gsm[j].signalStrengthGsm));
|
||||
}
|
||||
continue;
|
||||
case RADIO_CELL_INFO_LTE:
|
||||
lte = cell->lte.data.ptr;
|
||||
for (j = 0; j < cell->lte.count; j++) {
|
||||
g_ptr_array_add(l, binder_cell_info_new_cell_lte(registered,
|
||||
<e[j].cellIdentityLte.base,
|
||||
<e[j].signalStrengthLte));
|
||||
}
|
||||
continue;
|
||||
case RADIO_CELL_INFO_WCDMA:
|
||||
wcdma = cell->wcdma.data.ptr;
|
||||
for (j = 0; j < cell->wcdma.count; j++) {
|
||||
g_ptr_array_add(l, binder_cell_info_new_cell_wcdma(registered,
|
||||
&wcdma[j].cellIdentityWcdma.base,
|
||||
&wcdma[j].signalStrengthWcdma.base));
|
||||
}
|
||||
continue;
|
||||
case RADIO_CELL_INFO_CDMA:
|
||||
case RADIO_CELL_INFO_TD_SCDMA:
|
||||
break;
|
||||
}
|
||||
DBG("unsupported cell type %d", cell->cellInfoType);
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
static
|
||||
GPtrArray*
|
||||
binder_cell_info_array_new_1_4(
|
||||
const RadioCellInfo_1_4* cells,
|
||||
gsize count)
|
||||
{
|
||||
gsize i;
|
||||
GPtrArray* l = g_ptr_array_sized_new(count + 1);
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
const RadioCellInfo_1_4* cell = cells + i;
|
||||
const gboolean registered = cell->registered;
|
||||
|
||||
switch ((RADIO_CELL_INFO_TYPE_1_4)cell->cellInfoType) {
|
||||
case RADIO_CELL_INFO_1_4_GSM:
|
||||
g_ptr_array_add(l, binder_cell_info_new_cell_gsm(registered,
|
||||
&cell->info.gsm.cellIdentityGsm.base,
|
||||
&cell->info.gsm.signalStrengthGsm));
|
||||
continue;
|
||||
case RADIO_CELL_INFO_1_4_LTE:
|
||||
g_ptr_array_add(l, binder_cell_info_new_cell_lte(registered,
|
||||
&cell->info.lte.base.cellIdentityLte.base,
|
||||
&cell->info.lte.base.signalStrengthLte));
|
||||
continue;
|
||||
case RADIO_CELL_INFO_1_4_WCDMA:
|
||||
g_ptr_array_add(l, binder_cell_info_new_cell_wcdma(registered,
|
||||
&cell->info.wcdma.cellIdentityWcdma.base,
|
||||
&cell->info.wcdma.signalStrengthWcdma.base));
|
||||
continue;
|
||||
case RADIO_CELL_INFO_1_4_TD_SCDMA:
|
||||
case RADIO_CELL_INFO_1_4_CDMA:
|
||||
case RADIO_CELL_INFO_1_4_NR:
|
||||
break;
|
||||
}
|
||||
DBG("unsupported cell type %d", cell->cellInfoType);
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_cell_info_list_1_0(
|
||||
BinderCellInfo* self,
|
||||
GBinderReader* reader)
|
||||
{
|
||||
gsize count;
|
||||
const RadioCellInfo* cells = gbinder_reader_read_hidl_type_vec(reader,
|
||||
RadioCellInfo, &count);
|
||||
|
||||
if (cells) {
|
||||
binder_cell_info_update_cells(self,
|
||||
binder_cell_info_array_new_1_0(cells, count));
|
||||
} else {
|
||||
ofono_warn("Failed to parse cellInfoList payload");
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_cell_info_list_1_2(
|
||||
BinderCellInfo* self,
|
||||
GBinderReader* reader)
|
||||
{
|
||||
gsize count;
|
||||
const RadioCellInfo_1_2* cells = gbinder_reader_read_hidl_type_vec(reader,
|
||||
RadioCellInfo_1_2, &count);
|
||||
|
||||
if (cells) {
|
||||
binder_cell_info_update_cells(self,
|
||||
binder_cell_info_array_new_1_2(cells, count));
|
||||
} else {
|
||||
ofono_warn("Failed to parse cellInfoList_1_2 payload");
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_cell_info_list_1_4(
|
||||
BinderCellInfo* self,
|
||||
GBinderReader* reader)
|
||||
{
|
||||
gsize count;
|
||||
const RadioCellInfo_1_4* cells = gbinder_reader_read_hidl_type_vec(reader,
|
||||
RadioCellInfo_1_4, &count);
|
||||
|
||||
if (cells) {
|
||||
binder_cell_info_update_cells(self,
|
||||
binder_cell_info_array_new_1_4(cells, count));
|
||||
} else {
|
||||
ofono_warn("Failed to parse cellInfoList_1_4 payload");
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_cell_info_list_changed_1_0(
|
||||
RadioClient* client,
|
||||
RADIO_IND code,
|
||||
const GBinderReader* args,
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderCellInfo* self = THIS(user_data);
|
||||
|
||||
GASSERT(code == RADIO_IND_CELL_INFO_LIST);
|
||||
if (self->enabled) {
|
||||
GBinderReader reader;
|
||||
|
||||
gbinder_reader_copy(&reader, args);
|
||||
binder_cell_info_list_1_0(self, &reader);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_cell_info_list_changed_1_2(
|
||||
RadioClient* client,
|
||||
RADIO_IND code,
|
||||
const GBinderReader* args,
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderCellInfo* self = THIS(user_data);
|
||||
|
||||
GASSERT(code == RADIO_IND_CELL_INFO_LIST_1_2);
|
||||
if (self->enabled) {
|
||||
GBinderReader reader;
|
||||
|
||||
gbinder_reader_copy(&reader, args);
|
||||
binder_cell_info_list_1_2(self, &reader);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_cell_info_list_changed_1_4(
|
||||
RadioClient* client,
|
||||
RADIO_IND code,
|
||||
const GBinderReader* args,
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderCellInfo* self = THIS(user_data);
|
||||
|
||||
GASSERT(code == RADIO_IND_CELL_INFO_LIST_1_4);
|
||||
if (self->enabled) {
|
||||
GBinderReader reader;
|
||||
|
||||
gbinder_reader_copy(&reader, args);
|
||||
binder_cell_info_list_1_4(self, &reader);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_cell_info_list_cb(
|
||||
RadioRequest* req,
|
||||
RADIO_TX_STATUS status,
|
||||
RADIO_RESP resp,
|
||||
RADIO_ERROR error,
|
||||
const GBinderReader* args,
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderCellInfo* self = THIS(user_data);
|
||||
|
||||
GASSERT(self->query_req == req);
|
||||
radio_request_drop(self->query_req);
|
||||
self->query_req = NULL;
|
||||
|
||||
if (status == RADIO_TX_STATUS_OK) {
|
||||
if (error == RADIO_ERROR_NONE) {
|
||||
if (self->enabled) {
|
||||
GBinderReader reader;
|
||||
|
||||
gbinder_reader_copy(&reader, args);
|
||||
switch (resp) {
|
||||
case RADIO_RESP_GET_CELL_INFO_LIST:
|
||||
binder_cell_info_list_1_0(self, &reader);
|
||||
break;
|
||||
case RADIO_RESP_GET_CELL_INFO_LIST_1_2:
|
||||
binder_cell_info_list_1_2(self, &reader);
|
||||
break;
|
||||
case RADIO_RESP_GET_CELL_INFO_LIST_1_4:
|
||||
binder_cell_info_list_1_4(self, &reader);
|
||||
break;
|
||||
default:
|
||||
ofono_warn("Unexpected getCellInfoList response %d", resp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
DBG_(self, "%s error %d", radio_resp_name(resp), error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_cell_info_set_rate_cb(
|
||||
RadioRequest* req,
|
||||
RADIO_TX_STATUS status,
|
||||
RADIO_RESP resp,
|
||||
RADIO_ERROR error,
|
||||
const GBinderReader* args,
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderCellInfo* self = THIS(user_data);
|
||||
|
||||
DBG_(self, "");
|
||||
GASSERT(self->set_rate_req == req);
|
||||
radio_request_drop(self->set_rate_req);
|
||||
self->set_rate_req = NULL;
|
||||
|
||||
if (status == RADIO_TX_STATUS_OK) {
|
||||
if (resp == RADIO_RESP_SET_CELL_INFO_LIST_RATE) {
|
||||
if (error != RADIO_ERROR_NONE) {
|
||||
DBG_(self, "Failed to set cell info rate, error %d", error);
|
||||
}
|
||||
} else {
|
||||
ofono_error("Unexpected setCellInfoListRate response %d", resp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
gboolean
|
||||
binder_cell_info_retry(
|
||||
RadioRequest* req,
|
||||
RADIO_TX_STATUS status,
|
||||
RADIO_RESP resp,
|
||||
RADIO_ERROR error,
|
||||
const GBinderReader* args,
|
||||
void* user_data)
|
||||
{
|
||||
BinderCellInfo* self = THIS(user_data);
|
||||
|
||||
switch (error) {
|
||||
case RADIO_ERROR_NONE:
|
||||
case RADIO_ERROR_RADIO_NOT_AVAILABLE:
|
||||
return FALSE;
|
||||
default:
|
||||
return self->enabled;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_cell_info_query(
|
||||
BinderCellInfo* self)
|
||||
{
|
||||
radio_request_drop(self->query_req);
|
||||
self->query_req = radio_request_new(self->client,
|
||||
RADIO_REQ_GET_CELL_INFO_LIST, NULL,
|
||||
binder_cell_info_list_cb, NULL, self);
|
||||
radio_request_set_retry(self->query_req, BINDER_RETRY_MS, MAX_RETRIES);
|
||||
radio_request_set_retry_func(self->query_req, binder_cell_info_retry);
|
||||
radio_request_submit(self->query_req);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_cell_info_set_rate(
|
||||
BinderCellInfo* self)
|
||||
{
|
||||
GBinderWriter writer;
|
||||
|
||||
radio_request_drop(self->set_rate_req);
|
||||
self->set_rate_req = radio_request_new(self->client,
|
||||
RADIO_REQ_SET_CELL_INFO_LIST_RATE, &writer,
|
||||
binder_cell_info_set_rate_cb, NULL, self);
|
||||
|
||||
gbinder_writer_append_int32(&writer,
|
||||
(self->update_rate_ms >= 0 && self->enabled) ?
|
||||
self->update_rate_ms : INT_MAX);
|
||||
|
||||
radio_request_set_retry(self->set_rate_req, BINDER_RETRY_MS, MAX_RETRIES);
|
||||
radio_request_set_retry_func(self->set_rate_req, binder_cell_info_retry);
|
||||
radio_request_submit(self->set_rate_req);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_cell_info_refresh(
|
||||
BinderCellInfo* self)
|
||||
{
|
||||
/* getCellInfoList fails without SIM card */
|
||||
if (self->enabled &&
|
||||
self->radio->state == RADIO_STATE_ON &&
|
||||
self->sim_card_ready) {
|
||||
binder_cell_info_query(self);
|
||||
} else {
|
||||
binder_cell_info_clear(self);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_cell_info_radio_state_cb(
|
||||
BinderRadio* radio,
|
||||
BINDER_RADIO_PROPERTY property,
|
||||
void* user_data)
|
||||
{
|
||||
BinderCellInfo* self = THIS(user_data);
|
||||
|
||||
DBG_(self, "%s", binder_radio_state_string(radio->state));
|
||||
binder_cell_info_refresh(self);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_cell_info_sim_status_cb(
|
||||
BinderSimCard* sim,
|
||||
void* user_data)
|
||||
{
|
||||
BinderCellInfo* self = THIS(user_data);
|
||||
|
||||
self->sim_card_ready = binder_sim_card_ready(sim);
|
||||
DBG_(self, "%sready", self->sim_card_ready ? "" : "not ");
|
||||
binder_cell_info_refresh(self);
|
||||
if (self->sim_card_ready) {
|
||||
binder_cell_info_set_rate(self);
|
||||
}
|
||||
}
|
||||
|
||||
/*==========================================================================*
|
||||
* ofono_cell_info interface
|
||||
*==========================================================================*/
|
||||
|
||||
typedef struct binder_cell_info_closure {
|
||||
GCClosure cclosure;
|
||||
ofono_cell_info_cb_t cb;
|
||||
void* user_data;
|
||||
} BinderCellInfoClosure;
|
||||
|
||||
static inline BinderCellInfo* binder_cell_info_cast(struct ofono_cell_info* info)
|
||||
{ return G_CAST(info, BinderCellInfo, info); }
|
||||
|
||||
static
|
||||
void
|
||||
binder_cell_info_ref_proc(
|
||||
struct ofono_cell_info* info)
|
||||
{
|
||||
g_object_ref(binder_cell_info_cast(info));
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_cell_info_unref_proc(
|
||||
struct ofono_cell_info* info)
|
||||
{
|
||||
g_object_unref(binder_cell_info_cast(info));
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_cell_info_cells_changed_cb(
|
||||
BinderCellInfo* self,
|
||||
BinderCellInfoClosure* closure)
|
||||
{
|
||||
closure->cb(&self->info, closure->user_data);
|
||||
}
|
||||
|
||||
static
|
||||
gulong
|
||||
binder_cell_info_add_cells_changed_handler_proc(
|
||||
struct ofono_cell_info* info,
|
||||
ofono_cell_info_cb_t cb,
|
||||
void* user_data)
|
||||
{
|
||||
if (cb) {
|
||||
BinderCellInfoClosure* closure = (BinderCellInfoClosure *)
|
||||
g_closure_new_simple(sizeof(BinderCellInfoClosure), NULL);
|
||||
GCClosure* cc = &closure->cclosure;
|
||||
|
||||
cc->closure.data = closure;
|
||||
cc->callback = G_CALLBACK(binder_cell_info_cells_changed_cb);
|
||||
closure->cb = cb;
|
||||
closure->user_data = user_data;
|
||||
return g_signal_connect_closure_by_id(binder_cell_info_cast(info),
|
||||
binder_cell_info_signals[SIGNAL_CELLS_CHANGED], 0,
|
||||
&cc->closure, FALSE);
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_cell_info_remove_handler_proc(
|
||||
struct ofono_cell_info* info,
|
||||
gulong id)
|
||||
{
|
||||
if (G_LIKELY(id)) {
|
||||
g_signal_handler_disconnect(binder_cell_info_cast(info), id);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_cell_info_set_update_interval_proc(
|
||||
struct ofono_cell_info* info,
|
||||
int ms)
|
||||
{
|
||||
BinderCellInfo* self = binder_cell_info_cast(info);
|
||||
|
||||
if (self->update_rate_ms != ms) {
|
||||
self->update_rate_ms = ms;
|
||||
DBG_(self, "%d ms", ms);
|
||||
if (self->enabled && self->sim_card_ready) {
|
||||
binder_cell_info_set_rate(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_cell_info_set_enabled_proc(
|
||||
struct ofono_cell_info* info,
|
||||
gboolean enabled)
|
||||
{
|
||||
BinderCellInfo* self = binder_cell_info_cast(info);
|
||||
|
||||
if (self->enabled != enabled) {
|
||||
self->enabled = enabled;
|
||||
DBG_(self, "%d", enabled);
|
||||
binder_cell_info_refresh(self);
|
||||
if (self->sim_card_ready) {
|
||||
binder_cell_info_set_rate(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*==========================================================================*
|
||||
* API
|
||||
*==========================================================================*/
|
||||
|
||||
struct ofono_cell_info*
|
||||
binder_cell_info_new(
|
||||
RadioClient* client,
|
||||
const char* log_prefix,
|
||||
BinderRadio* radio,
|
||||
BinderSimCard* sim)
|
||||
{
|
||||
BinderCellInfo* self = g_object_new(THIS_TYPE, 0);
|
||||
|
||||
self->client = radio_client_ref(client);
|
||||
self->radio = binder_radio_ref(radio);
|
||||
self->sim_card = binder_sim_card_ref(sim);
|
||||
self->log_prefix = binder_dup_prefix(log_prefix);
|
||||
|
||||
DBG_(self, "");
|
||||
self->event_id[CELL_INFO_EVENT_1_0] =
|
||||
radio_client_add_indication_handler(client,
|
||||
RADIO_IND_CELL_INFO_LIST,
|
||||
binder_cell_info_list_changed_1_0, self);
|
||||
self->event_id[CELL_INFO_EVENT_1_2] =
|
||||
radio_client_add_indication_handler(client,
|
||||
RADIO_IND_CELL_INFO_LIST_1_2,
|
||||
binder_cell_info_list_changed_1_2, self);
|
||||
self->event_id[CELL_INFO_EVENT_1_4] =
|
||||
radio_client_add_indication_handler(client,
|
||||
RADIO_IND_CELL_INFO_LIST_1_4,
|
||||
binder_cell_info_list_changed_1_4, self);
|
||||
self->radio_state_event_id =
|
||||
binder_radio_add_property_handler(radio,
|
||||
BINDER_RADIO_PROPERTY_STATE,
|
||||
binder_cell_info_radio_state_cb, self);
|
||||
self->sim_status_event_id =
|
||||
binder_sim_card_add_status_changed_handler(sim,
|
||||
binder_cell_info_sim_status_cb, self);
|
||||
self->sim_card_ready = binder_sim_card_ready(sim);
|
||||
binder_cell_info_refresh(self);
|
||||
|
||||
/* Disable updates by default */
|
||||
self->enabled = FALSE;
|
||||
if (self->sim_card_ready) {
|
||||
binder_cell_info_set_rate(self);
|
||||
}
|
||||
return &self->info;
|
||||
}
|
||||
|
||||
/*==========================================================================*
|
||||
* Internals
|
||||
*==========================================================================*/
|
||||
|
||||
static
|
||||
void
|
||||
binder_cell_info_init(
|
||||
BinderCellInfo* self)
|
||||
{
|
||||
static const struct ofono_cell_info_proc binder_cell_info_proc = {
|
||||
binder_cell_info_ref_proc,
|
||||
binder_cell_info_unref_proc,
|
||||
binder_cell_info_add_cells_changed_handler_proc,
|
||||
binder_cell_info_remove_handler_proc,
|
||||
binder_cell_info_set_update_interval_proc,
|
||||
binder_cell_info_set_enabled_proc
|
||||
};
|
||||
|
||||
self->update_rate_ms = DEFAULT_UPDATE_RATE_MS;
|
||||
self->info.cells = self->cells = g_new0(struct ofono_cell*, 1);
|
||||
self->info.proc = &binder_cell_info_proc;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_cell_info_finalize(
|
||||
GObject* object)
|
||||
{
|
||||
BinderCellInfo* self = THIS(object);
|
||||
|
||||
DBG_(self, "");
|
||||
radio_request_drop(self->query_req);
|
||||
radio_request_drop(self->set_rate_req);
|
||||
radio_client_remove_all_handlers(self->client, self->event_id);
|
||||
radio_client_unref(self->client);
|
||||
binder_radio_remove_handler(self->radio, self->radio_state_event_id);
|
||||
binder_radio_unref(self->radio);
|
||||
binder_sim_card_remove_handler(self->sim_card, self->sim_status_event_id);
|
||||
binder_sim_card_unref(self->sim_card);
|
||||
gutil_ptrv_free((void**)self->cells);
|
||||
g_free(self->log_prefix);
|
||||
G_OBJECT_CLASS(PARENT_CLASS)->finalize(object);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_cell_info_class_init(
|
||||
BinderCellInfoClass* klass)
|
||||
{
|
||||
G_OBJECT_CLASS(klass)->finalize = binder_cell_info_finalize;
|
||||
binder_cell_info_signals[SIGNAL_CELLS_CHANGED] =
|
||||
g_signal_new(SIGNAL_CELLS_CHANGED_NAME, G_OBJECT_CLASS_TYPE(klass),
|
||||
G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
39
src/binder_cell_info.h
Normal file
39
src/binder_cell_info.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef BINDER_CELL_INFO_H
|
||||
#define BINDER_CELL_INFO_H
|
||||
|
||||
#include "binder_types.h"
|
||||
|
||||
#include <ofono/cell-info.h>
|
||||
|
||||
struct ofono_cell_info*
|
||||
binder_cell_info_new(
|
||||
RadioClient* client,
|
||||
const char* log_prefix,
|
||||
BinderRadio* radio,
|
||||
BinderSimCard* sim)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
#endif /* BINDER_CELL_INFO_H */
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
627
src/binder_connman.c
Normal file
627
src/binder_connman.c
Normal file
@@ -0,0 +1,627 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2019-2021 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "binder_base.h"
|
||||
#include "binder_connman.h"
|
||||
|
||||
#include <ofono/log.h>
|
||||
#include <ofono/gdbus.h>
|
||||
|
||||
#include <gutil_macros.h>
|
||||
#include <gutil_misc.h>
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
BINDER_BASE_ASSERT_COUNT(BINDER_CONNMAN_PROPERTY_COUNT);
|
||||
|
||||
#define CONNMAN_BUS DBUS_BUS_SYSTEM
|
||||
#define CONNMAN_SERVICE "net.connman"
|
||||
#define CONNMAN_PATH "/"
|
||||
|
||||
#define CONNMAN_GET_PROPERTIES "GetProperties"
|
||||
#define CONNMAN_GET_TECHNOLOGIES "GetTechnologies"
|
||||
#define CONNMAN_PROPERTY_CHANGED "PropertyChanged"
|
||||
#define CONNMAN_TECH_CONNECTED "Connected"
|
||||
#define CONNMAN_TECH_TETHERING "Tethering"
|
||||
|
||||
#define CONNMAN_INTERFACE_(name) "net.connman." name
|
||||
#define CONNMAN_MANAGER_INTERFACE CONNMAN_INTERFACE_("Manager")
|
||||
#define CONNMAN_TECH_INTERFACE CONNMAN_INTERFACE_("Technology")
|
||||
|
||||
#define CONNMAN_TECH_PATH_(name) "/net/connman/technology/" name
|
||||
#define CONNMAN_TECH_PATH_WIFI CONNMAN_TECH_PATH_("wifi")
|
||||
|
||||
#define CONNMAN_TECH_CONNECTED_BIT (0x01)
|
||||
#define CONNMAN_TECH_TETHERING_BIT (0x02)
|
||||
#define CONNMAN_TECH_ALL_PROPERTY_BITS (\
|
||||
CONNMAN_TECH_CONNECTED_BIT | \
|
||||
CONNMAN_TECH_TETHERING_BIT)
|
||||
|
||||
typedef BinderBaseClass ConnManObjectClass;
|
||||
|
||||
typedef struct connman_tech ConnManTech;
|
||||
|
||||
typedef struct connman_object {
|
||||
BinderBase base;
|
||||
BinderConnman pub;
|
||||
DBusConnection* connection;
|
||||
DBusPendingCall* call;
|
||||
guint service_watch;
|
||||
guint signal_watch;
|
||||
GHashTable* techs;
|
||||
ConnManTech* wifi;
|
||||
} ConnManObject;
|
||||
|
||||
GType connman_object_get_type() BINDER_INTERNAL;
|
||||
G_DEFINE_TYPE(ConnManObject, connman_object, BINDER_TYPE_BASE)
|
||||
#define PARENT_CLASS connman_object_parent_class
|
||||
#define THIS_TYPE connman_object_get_type()
|
||||
#define THIS(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, THIS_TYPE, ConnManObject)
|
||||
|
||||
struct connman_tech {
|
||||
ConnManObject* obj;
|
||||
const char* path;
|
||||
gboolean connected;
|
||||
gboolean tethering;
|
||||
};
|
||||
|
||||
#define SIGNAL_BIT_(name) \
|
||||
BINDER_BASE_PROPERTY_BIT(BINDER_CONNMAN_PROPERTY_##name)
|
||||
|
||||
static inline
|
||||
ConnManObject*
|
||||
connman_object_cast(
|
||||
BinderConnman* connman)
|
||||
{
|
||||
return G_LIKELY(connman) ?
|
||||
THIS(G_CAST(connman, ConnManObject, pub)) :
|
||||
NULL;
|
||||
}
|
||||
|
||||
static inline
|
||||
const char*
|
||||
connman_iter_get_string(
|
||||
DBusMessageIter* it)
|
||||
{
|
||||
const char* str = NULL;
|
||||
|
||||
dbus_message_iter_get_basic(it, &str);
|
||||
return str;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
connman_object_emit_pending_signals(
|
||||
ConnManObject* self)
|
||||
{
|
||||
BinderBase* base = &self->base;
|
||||
BinderConnman* connman = &self->pub;
|
||||
gsize late_signals = 0;
|
||||
|
||||
/* Handlers could drop their references to us */
|
||||
g_object_ref(self);
|
||||
|
||||
/*
|
||||
* PRESENT and VALID are the last signals to be emitted if the object
|
||||
* BECOMES present and/or valid.
|
||||
*/
|
||||
if ((base->queued_signals & SIGNAL_BIT_(VALID)) && connman->valid) {
|
||||
base->queued_signals &= ~SIGNAL_BIT_(VALID);
|
||||
late_signals |= SIGNAL_BIT_(VALID);
|
||||
}
|
||||
if ((base->queued_signals & SIGNAL_BIT_(PRESENT)) && connman->present) {
|
||||
base->queued_signals &= ~SIGNAL_BIT_(PRESENT);
|
||||
late_signals |= SIGNAL_BIT_(PRESENT);
|
||||
}
|
||||
|
||||
/*
|
||||
* Emit the signals. Not that in case if valid has become FALSE,
|
||||
* then VALID is emitted first, otherwise it's emitted last.
|
||||
* Same thing with PRESENT.
|
||||
*/
|
||||
binder_base_emit_queued_signals(base);
|
||||
base->queued_signals |= late_signals;
|
||||
binder_base_emit_queued_signals(base);
|
||||
|
||||
/* And release the temporary reference */
|
||||
g_object_unref(self);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
connman_cancel_call(
|
||||
ConnManObject* self)
|
||||
{
|
||||
if (self->call) {
|
||||
dbus_pending_call_cancel(self->call);
|
||||
dbus_pending_call_unref(self->call);
|
||||
self->call = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
ConnManTech*
|
||||
connman_tech_new(
|
||||
ConnManObject* self,
|
||||
const char* path)
|
||||
{
|
||||
ConnManTech* tech = g_new0(ConnManTech, 1);
|
||||
char* key = g_strdup(path);
|
||||
|
||||
tech->obj = self;
|
||||
tech->path = key;
|
||||
g_hash_table_replace(self->techs, key, tech);
|
||||
return tech;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
connman_invalidate(
|
||||
ConnManObject* self)
|
||||
{
|
||||
BinderConnman* connman = &self->pub;
|
||||
|
||||
if (connman->valid) {
|
||||
connman->valid = FALSE;
|
||||
binder_base_queue_property_change(&self->base,
|
||||
BINDER_CONNMAN_PROPERTY_VALID);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
connman_update_valid(
|
||||
ConnManObject* self)
|
||||
{
|
||||
BinderConnman* connman = &self->pub;
|
||||
const gboolean valid = (connman->present && !self->call);
|
||||
|
||||
if (connman->valid != valid) {
|
||||
connman->valid = valid;
|
||||
binder_base_queue_property_change(&self->base,
|
||||
BINDER_CONNMAN_PROPERTY_VALID);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
gboolean
|
||||
connman_update_tethering(
|
||||
ConnManObject* self)
|
||||
{
|
||||
BinderConnman* connman = &self->pub;
|
||||
gboolean tethering = FALSE;
|
||||
GHashTableIter it;
|
||||
gpointer value;
|
||||
|
||||
g_hash_table_iter_init(&it, self->techs);
|
||||
while (g_hash_table_iter_next(&it, NULL, &value)) {
|
||||
const ConnManTech* tech = value;
|
||||
|
||||
if (tech->tethering) {
|
||||
tethering = TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (connman->tethering != tethering) {
|
||||
connman->tethering = tethering;
|
||||
binder_base_queue_property_change(&self->base,
|
||||
BINDER_CONNMAN_PROPERTY_TETHERING);
|
||||
return TRUE;
|
||||
} else {
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
connman_set_tech_tethering(
|
||||
ConnManTech* tech,
|
||||
gboolean tethering)
|
||||
{
|
||||
if (tech->tethering != tethering) {
|
||||
ConnManObject* self = tech->obj;
|
||||
|
||||
tech->tethering = tethering;
|
||||
DBG(CONNMAN_TECH_TETHERING " %s for %s", tethering ? "on" : "off",
|
||||
tech->path);
|
||||
if (tethering) {
|
||||
BinderConnman* connman = &self->pub;
|
||||
|
||||
if (G_LIKELY(!connman->tethering)) {
|
||||
/* Definitely tethering now */
|
||||
connman->tethering = TRUE;
|
||||
binder_base_queue_property_change(&self->base,
|
||||
BINDER_CONNMAN_PROPERTY_TETHERING);
|
||||
DBG("Tethering on");
|
||||
}
|
||||
} else if (connman_update_tethering(self)) {
|
||||
/* Not tethering anymore */
|
||||
DBG("Tethering off");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
connman_set_tech_connected(
|
||||
ConnManTech* tech,
|
||||
gboolean connected)
|
||||
{
|
||||
if (tech->connected != connected) {
|
||||
ConnManObject* self = tech->obj;
|
||||
|
||||
tech->connected = connected;
|
||||
DBG(CONNMAN_TECH_CONNECTED " %s for %s", connected ? "on" : "off",
|
||||
tech->path);
|
||||
if (tech == self->wifi) {
|
||||
BinderConnman* connman = &self->pub;
|
||||
|
||||
connman->wifi_connected = connected;
|
||||
binder_base_queue_property_change(&self->base,
|
||||
BINDER_CONNMAN_PROPERTY_WIFI_CONNECTED);
|
||||
DBG("WiFi %sconnected", connected ? "" : "dis");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
connman_tech_set_property(
|
||||
ConnManTech* tech,
|
||||
DBusMessageIter* it)
|
||||
{
|
||||
DBusMessageIter var;
|
||||
DBusBasicValue value;
|
||||
const char* key = connman_iter_get_string(it);
|
||||
|
||||
dbus_message_iter_next(it);
|
||||
dbus_message_iter_recurse(it, &var);
|
||||
dbus_message_iter_get_basic(&var, &value);
|
||||
if (!g_ascii_strcasecmp(key, CONNMAN_TECH_CONNECTED)) {
|
||||
if (dbus_message_iter_get_arg_type(&var) == DBUS_TYPE_BOOLEAN) {
|
||||
connman_set_tech_connected(tech, value.bool_val);
|
||||
return CONNMAN_TECH_CONNECTED_BIT;
|
||||
}
|
||||
} else if (!g_ascii_strcasecmp(key, CONNMAN_TECH_TETHERING)) {
|
||||
if (dbus_message_iter_get_arg_type(&var) == DBUS_TYPE_BOOLEAN) {
|
||||
connman_set_tech_tethering(tech, value.bool_val);
|
||||
return CONNMAN_TECH_TETHERING_BIT;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
connman_tech_set_properties(
|
||||
ConnManTech* tech,
|
||||
DBusMessageIter* it)
|
||||
{
|
||||
DBusMessageIter dict;
|
||||
int handled = 0;
|
||||
|
||||
dbus_message_iter_recurse(it, &dict);
|
||||
while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
|
||||
DBusMessageIter entry;
|
||||
|
||||
dbus_message_iter_recurse(&dict, &entry);
|
||||
handled |= connman_tech_set_property(tech, &entry);
|
||||
if (handled == CONNMAN_TECH_ALL_PROPERTY_BITS) {
|
||||
/* Ignore the rest */
|
||||
break;
|
||||
}
|
||||
dbus_message_iter_next(&dict);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
gboolean
|
||||
connman_tech_property_changed(
|
||||
DBusConnection* conn,
|
||||
DBusMessage* msg,
|
||||
void* user_data)
|
||||
{
|
||||
const char* path = dbus_message_get_path(msg);
|
||||
ConnManObject* self = THIS(user_data);
|
||||
ConnManTech* tech = g_hash_table_lookup(self->techs, path);
|
||||
DBusMessageIter it;
|
||||
|
||||
if (tech && dbus_message_has_signature(msg, "sv") &&
|
||||
dbus_message_iter_init(msg, &it)) {
|
||||
const char* name = connman_iter_get_string(&it);
|
||||
|
||||
if (!connman_tech_set_property(tech, &it)) {
|
||||
DBG("%s changed for %s", name, path);
|
||||
}
|
||||
connman_object_emit_pending_signals(self);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
connman_set_techs(
|
||||
ConnManObject* self,
|
||||
DBusMessageIter* it)
|
||||
{
|
||||
DBusMessageIter list;
|
||||
|
||||
dbus_message_iter_recurse(it, &list);
|
||||
while (dbus_message_iter_get_arg_type(&list) == DBUS_TYPE_STRUCT) {
|
||||
DBusMessageIter entry;
|
||||
const char* path;
|
||||
ConnManTech* tech;
|
||||
|
||||
dbus_message_iter_recurse(&list, &entry);
|
||||
path = connman_iter_get_string(&entry);
|
||||
tech = connman_tech_new(self, path);
|
||||
|
||||
DBG("%s", path);
|
||||
if (!g_strcmp0(path, CONNMAN_TECH_PATH_WIFI)) {
|
||||
/* WiFi is a special case */
|
||||
self->wifi = tech;
|
||||
}
|
||||
|
||||
dbus_message_iter_next(&entry);
|
||||
connman_tech_set_properties(tech, &entry);
|
||||
dbus_message_iter_next(&list);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
connman_techs_reply(
|
||||
DBusPendingCall* call,
|
||||
void* user_data)
|
||||
{
|
||||
ConnManObject* self = THIS(user_data);
|
||||
DBusMessage* reply = dbus_pending_call_steal_reply(call);
|
||||
DBusError error;
|
||||
DBusMessageIter array;
|
||||
|
||||
dbus_error_init(&error);
|
||||
if (dbus_set_error_from_message(&error, reply)) {
|
||||
DBG("Failed to get technologies: %s", error.message);
|
||||
dbus_error_free(&error);
|
||||
} else if (dbus_message_has_signature(reply, "a(oa{sv})") &&
|
||||
dbus_message_iter_init(reply, &array)) {
|
||||
connman_set_techs(self, &array);
|
||||
}
|
||||
|
||||
dbus_message_unref(reply);
|
||||
dbus_pending_call_unref(self->call);
|
||||
self->call = NULL;
|
||||
connman_update_valid(self);
|
||||
connman_object_emit_pending_signals(self);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
connman_get_techs(
|
||||
ConnManObject* self)
|
||||
{
|
||||
DBusMessage* msg = dbus_message_new_method_call(CONNMAN_SERVICE,
|
||||
CONNMAN_PATH, CONNMAN_MANAGER_INTERFACE, CONNMAN_GET_TECHNOLOGIES);
|
||||
|
||||
connman_cancel_call(self);
|
||||
if (g_dbus_send_message_with_reply(self->connection, msg, &self->call,
|
||||
DBUS_TIMEOUT_INFINITE)) {
|
||||
/* Not valid while any request is pending */
|
||||
connman_invalidate(self);
|
||||
dbus_pending_call_set_notify(self->call, connman_techs_reply,
|
||||
self, NULL);
|
||||
}
|
||||
dbus_message_unref(msg);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
connman_appeared(
|
||||
DBusConnection* conn,
|
||||
void* user_data)
|
||||
{
|
||||
ConnManObject* self = THIS(user_data);
|
||||
BinderConnman* connman = &self->pub;
|
||||
|
||||
if (!connman->present) {
|
||||
DBG("connman is there");
|
||||
connman->present = TRUE;
|
||||
binder_base_queue_property_change(&self->base,
|
||||
BINDER_CONNMAN_PROPERTY_PRESENT);
|
||||
connman_get_techs(self);
|
||||
connman_object_emit_pending_signals(self);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
connman_vanished(
|
||||
DBusConnection* conn,
|
||||
void* user_data)
|
||||
{
|
||||
ConnManObject* self = THIS(user_data);
|
||||
BinderConnman* connman = &self->pub;
|
||||
|
||||
if (connman->present) {
|
||||
DBG("connman has disappeared");
|
||||
g_hash_table_remove_all(self->techs);
|
||||
self->wifi = NULL;
|
||||
connman->present = FALSE;
|
||||
binder_base_queue_property_change(&self->base,
|
||||
BINDER_CONNMAN_PROPERTY_PRESENT);
|
||||
if (connman->wifi_connected) {
|
||||
connman->wifi_connected = FALSE;
|
||||
binder_base_queue_property_change(&self->base,
|
||||
BINDER_CONNMAN_PROPERTY_WIFI_CONNECTED);
|
||||
}
|
||||
if (connman->tethering) {
|
||||
connman->tethering = FALSE;
|
||||
binder_base_queue_property_change(&self->base,
|
||||
BINDER_CONNMAN_PROPERTY_TETHERING);
|
||||
}
|
||||
connman_object_emit_pending_signals(self);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
connman_init(
|
||||
ConnManObject* self,
|
||||
DBusConnection* connection)
|
||||
{
|
||||
self->connection = dbus_connection_ref(connection);
|
||||
self->service_watch = g_dbus_add_service_watch(self->connection,
|
||||
CONNMAN_SERVICE, connman_appeared, connman_vanished, self, NULL);
|
||||
self->signal_watch = g_dbus_add_signal_watch(self->connection,
|
||||
CONNMAN_SERVICE, NULL, CONNMAN_TECH_INTERFACE,
|
||||
CONNMAN_PROPERTY_CHANGED, connman_tech_property_changed, self, NULL);
|
||||
}
|
||||
|
||||
/*==========================================================================*
|
||||
* API
|
||||
*==========================================================================*/
|
||||
|
||||
BinderConnman*
|
||||
binder_connman_new()
|
||||
{
|
||||
static ConnManObject* instance = NULL;
|
||||
|
||||
if (instance) {
|
||||
g_object_ref(instance);
|
||||
return &instance->pub;
|
||||
} else {
|
||||
DBusError error;
|
||||
DBusConnection* connection;
|
||||
|
||||
dbus_error_init(&error);
|
||||
connection = dbus_bus_get(CONNMAN_BUS, NULL);
|
||||
|
||||
if (connection) {
|
||||
instance = g_object_new(THIS_TYPE, NULL);
|
||||
connman_init(instance, connection);
|
||||
dbus_connection_unref(connection);
|
||||
g_object_add_weak_pointer(G_OBJECT(instance),
|
||||
(gpointer*)(&instance));
|
||||
return &instance->pub;
|
||||
} else {
|
||||
ofono_error("Unable to attach to connman bus: %s", error.message);
|
||||
dbus_error_free(&error);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BinderConnman*
|
||||
binder_connman_ref(
|
||||
BinderConnman* connman)
|
||||
{
|
||||
ConnManObject* self = connman_object_cast(connman);
|
||||
|
||||
if (G_LIKELY(self)) {
|
||||
g_object_ref(self);
|
||||
}
|
||||
return connman;
|
||||
}
|
||||
|
||||
void
|
||||
binder_connman_unref(
|
||||
BinderConnman* connman)
|
||||
{
|
||||
ConnManObject* self = connman_object_cast(connman);
|
||||
|
||||
if (G_LIKELY(self)) {
|
||||
g_object_unref(self);
|
||||
}
|
||||
}
|
||||
|
||||
gulong
|
||||
binder_connman_add_property_changed_handler(
|
||||
BinderConnman* connman,
|
||||
BINDER_CONNMAN_PROPERTY property,
|
||||
BinderConnmanPropertyFunc callback,
|
||||
void* user_data)
|
||||
{
|
||||
ConnManObject* self = connman_object_cast(connman);
|
||||
|
||||
return G_LIKELY(self) ? binder_base_add_property_handler(&self->base,
|
||||
property, G_CALLBACK(callback), user_data) : 0;
|
||||
}
|
||||
|
||||
void
|
||||
binder_connman_remove_handler(
|
||||
BinderConnman* connman,
|
||||
gulong id)
|
||||
{
|
||||
if (G_LIKELY(id)) {
|
||||
ConnManObject* self = connman_object_cast(connman);
|
||||
|
||||
if (G_LIKELY(self)) {
|
||||
g_signal_handler_disconnect(self, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
binder_connman_remove_handlers(
|
||||
BinderConnman* connman,
|
||||
gulong* ids,
|
||||
int n)
|
||||
{
|
||||
gutil_disconnect_handlers(connman_object_cast(connman), ids, n);
|
||||
}
|
||||
|
||||
/*==========================================================================*
|
||||
* Internals
|
||||
*==========================================================================*/
|
||||
|
||||
static
|
||||
void
|
||||
connman_object_init(
|
||||
ConnManObject* self)
|
||||
{
|
||||
self->techs = g_hash_table_new_full(g_str_hash, g_str_equal,
|
||||
g_free, g_free);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
connman_object_finalize(
|
||||
GObject *object)
|
||||
{
|
||||
ConnManObject* self = THIS(object);
|
||||
|
||||
connman_cancel_call(self);
|
||||
g_hash_table_destroy(self->techs);
|
||||
g_dbus_remove_watch(self->connection, self->service_watch);
|
||||
g_dbus_remove_watch(self->connection, self->signal_watch);
|
||||
dbus_connection_unref(self->connection);
|
||||
G_OBJECT_CLASS(PARENT_CLASS)->finalize(object);
|
||||
}
|
||||
|
||||
static void connman_object_class_init(ConnManObjectClass *klass)
|
||||
{
|
||||
G_OBJECT_CLASS(klass)->finalize = connman_object_finalize;
|
||||
BINDER_BASE_CLASS(klass)->public_offset =
|
||||
G_STRUCT_OFFSET(ConnManObject, pub);
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
91
src/binder_connman.h
Normal file
91
src/binder_connman.h
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2019-2021 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef BINDER_CONNMAN_H
|
||||
#define BINDER_CONNMAN_H
|
||||
|
||||
#include "binder_types.h"
|
||||
|
||||
typedef struct binder_connman {
|
||||
gboolean valid; /* TRUE if other fields are valid */
|
||||
gboolean present; /* ConnMan is present on D-Bus */
|
||||
gboolean tethering; /* At least one technology is tethering */
|
||||
gboolean wifi_connected; /* WiFi network is connected */
|
||||
} BinderConnman;
|
||||
|
||||
typedef enum binder_connman_property {
|
||||
BINDER_CONNMAN_PROPERTY_ANY,
|
||||
BINDER_CONNMAN_PROPERTY_VALID,
|
||||
BINDER_CONNMAN_PROPERTY_PRESENT,
|
||||
BINDER_CONNMAN_PROPERTY_TETHERING,
|
||||
BINDER_CONNMAN_PROPERTY_WIFI_CONNECTED,
|
||||
BINDER_CONNMAN_PROPERTY_COUNT
|
||||
} BINDER_CONNMAN_PROPERTY;
|
||||
|
||||
typedef
|
||||
void
|
||||
(*BinderConnmanPropertyFunc)(
|
||||
BinderConnman* connman,
|
||||
BINDER_CONNMAN_PROPERTY property,
|
||||
void* user_data);
|
||||
|
||||
BinderConnman*
|
||||
binder_connman_new(
|
||||
void)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
BinderConnman*
|
||||
binder_connman_ref(
|
||||
BinderConnman* connman)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_connman_unref(
|
||||
BinderConnman* connman)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
gulong
|
||||
binder_connman_add_property_changed_handler(
|
||||
BinderConnman* connman,
|
||||
BINDER_CONNMAN_PROPERTY property,
|
||||
BinderConnmanPropertyFunc fn,
|
||||
void* user_data)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_connman_remove_handler(
|
||||
BinderConnman* connman,
|
||||
gulong id)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_connman_remove_handlers(
|
||||
BinderConnman* connman,
|
||||
gulong* ids,
|
||||
int n)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
#define binder_connman_remove_all_handlers(connman, ids) \
|
||||
binder_connman_remove_handlers(connman, ids, G_N_ELEMENTS(ids))
|
||||
|
||||
#endif /* BINDER_CONNMAN_H */
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
2452
src/binder_data.c
Normal file
2452
src/binder_data.c
Normal file
File diff suppressed because it is too large
Load Diff
257
src/binder_data.h
Normal file
257
src/binder_data.h
Normal file
@@ -0,0 +1,257 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef BINDER_DATA_H
|
||||
#define BINDER_DATA_H
|
||||
|
||||
#include "binder_types.h"
|
||||
|
||||
#include <radio_request.h>
|
||||
|
||||
#include <ofono/slot.h>
|
||||
#include <ofono/gprs-context.h>
|
||||
|
||||
typedef enum binder_data_property {
|
||||
BINDER_DATA_PROPERTY_ANY,
|
||||
BINDER_DATA_PROPERTY_CALLS,
|
||||
BINDER_DATA_PROPERTY_ALLOWED,
|
||||
BINDER_DATA_PROPERTY_COUNT
|
||||
} BINDER_DATA_PROPERTY;
|
||||
|
||||
typedef struct binder_data_call {
|
||||
int cid;
|
||||
RADIO_DATA_CALL_FAIL_CAUSE status;
|
||||
RADIO_DATA_CALL_ACTIVE_STATUS active;
|
||||
enum ofono_gprs_proto prot;
|
||||
int retry_time;
|
||||
int mtu;
|
||||
char* ifname;
|
||||
char** dnses;
|
||||
char** gateways;
|
||||
char** addresses;
|
||||
char** pcscf;
|
||||
} BinderDataCall;
|
||||
|
||||
struct binder_data {
|
||||
GSList* calls;
|
||||
};
|
||||
|
||||
typedef enum binder_data_manager_flags {
|
||||
BINDER_DATA_MANAGER_NO_FLAGS = 0x00,
|
||||
BINDER_DATA_MANAGER_3GLTE_HANDOVER = 0x01
|
||||
} BINDER_DATA_MANAGER_FLAGS;
|
||||
|
||||
typedef enum binder_data_allow_data {
|
||||
BINDER_ALLOW_DATA_DISABLED,
|
||||
BINDER_ALLOW_DATA_ENABLED
|
||||
} BINDER_DATA_ALLOW_DATA;
|
||||
|
||||
typedef struct binder_data_options {
|
||||
BINDER_DATA_ALLOW_DATA allow_data;
|
||||
unsigned int data_call_retry_limit;
|
||||
unsigned int data_call_retry_delay_ms;
|
||||
} BinderDataOptions;
|
||||
|
||||
typedef struct binder_data_request BinderDataRequest;
|
||||
|
||||
typedef
|
||||
void
|
||||
(*BinderDataPropertyFunc)(
|
||||
BinderData* data,
|
||||
BINDER_DATA_PROPERTY property,
|
||||
void* user_data);
|
||||
|
||||
typedef
|
||||
void
|
||||
(*BinderDataCallSetupFunc)(
|
||||
BinderData* data,
|
||||
RADIO_ERROR status,
|
||||
const BinderDataCall* call,
|
||||
void* user_data);
|
||||
|
||||
typedef
|
||||
void
|
||||
(*BinderDataCallDeactivateFunc)(
|
||||
BinderData* data,
|
||||
RADIO_ERROR status,
|
||||
void* user_data);
|
||||
|
||||
BinderDataManager*
|
||||
binder_data_manager_new(
|
||||
RadioConfig* config,
|
||||
BINDER_DATA_MANAGER_FLAGS flags,
|
||||
enum ofono_radio_access_mode non_data_mode)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
BinderDataManager*
|
||||
binder_data_manager_ref(
|
||||
BinderDataManager* dm)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_data_manager_unref(
|
||||
BinderDataManager* dm)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_data_manager_check_data(
|
||||
BinderDataManager* dm)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_data_manager_assert_data_on(
|
||||
BinderDataManager* dm)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
gboolean
|
||||
binder_data_manager_need_set_data_allowed(
|
||||
BinderDataManager* dm)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
BinderData*
|
||||
binder_data_new(
|
||||
BinderDataManager* dm,
|
||||
RadioClient* client,
|
||||
const char* name,
|
||||
BinderRadio* radio,
|
||||
BinderNetwork* network,
|
||||
const BinderDataOptions* options,
|
||||
const BinderSlotConfig* config)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
BinderData*
|
||||
binder_data_ref(
|
||||
BinderData* data)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_data_unref(
|
||||
BinderData* data)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
gboolean
|
||||
binder_data_allowed(
|
||||
BinderData* data)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_data_poll_call_state(
|
||||
BinderData* data)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
gulong
|
||||
binder_data_add_property_handler(
|
||||
BinderData* data,
|
||||
BINDER_DATA_PROPERTY property,
|
||||
BinderDataPropertyFunc cb,
|
||||
void* user_data)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_data_remove_handler(
|
||||
BinderData* data,
|
||||
gulong id)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_data_allow(
|
||||
BinderData* data,
|
||||
enum ofono_slot_data_role role)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
BinderDataRequest*
|
||||
binder_data_call_setup(
|
||||
BinderData* data,
|
||||
const struct ofono_gprs_primary_context* ctx,
|
||||
enum ofono_gprs_context_type context_type,
|
||||
BinderDataCallSetupFunc cb,
|
||||
void* user_data)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
BinderDataRequest*
|
||||
binder_data_call_deactivate(
|
||||
BinderData* data,
|
||||
int cid,
|
||||
BinderDataCallDeactivateFunc cb,
|
||||
void* user_data)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_data_request_detach(
|
||||
BinderDataRequest* req)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_data_request_cancel(
|
||||
BinderDataRequest* req)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
gboolean
|
||||
binder_data_call_grab(
|
||||
BinderData* data,
|
||||
int cid,
|
||||
void* cookie)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_data_call_release(
|
||||
BinderData* data,
|
||||
int cid,
|
||||
void* cookie)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_data_call_free(
|
||||
BinderDataCall* call)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
BinderDataCall*
|
||||
binder_data_call_dup(
|
||||
const BinderDataCall* call)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
BinderDataCall*
|
||||
binder_data_call_find(
|
||||
GSList* list,
|
||||
int cid)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
RadioRequest*
|
||||
binder_data_deactivate_data_call_request_new(
|
||||
RadioRequestGroup* group,
|
||||
int cid,
|
||||
RadioRequestCompleteFunc complete,
|
||||
GDestroyNotify destroy,
|
||||
void* user_data)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
RadioRequest*
|
||||
binder_data_set_data_allowed_request_new(
|
||||
RadioRequestGroup* group,
|
||||
gboolean allow,
|
||||
RadioRequestCompleteFunc complete,
|
||||
GDestroyNotify destroy,
|
||||
void* user_data)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
#endif /* BINDER_DATA_H */
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
314
src/binder_devinfo.c
Normal file
314
src/binder_devinfo.c
Normal file
@@ -0,0 +1,314 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "binder_devinfo.h"
|
||||
#include "binder_modem.h"
|
||||
#include "binder_util.h"
|
||||
|
||||
#include <ofono/devinfo.h>
|
||||
#include <ofono/log.h>
|
||||
|
||||
#include <radio_request.h>
|
||||
#include <radio_request_group.h>
|
||||
|
||||
#include <gbinder_reader.h>
|
||||
|
||||
#include <gutil_idlequeue.h>
|
||||
#include <gutil_log.h>
|
||||
|
||||
enum binder_devinfo_cb_tag {
|
||||
DEVINFO_QUERY_SERIAL = 1,
|
||||
DEVINFO_QUERY_SVN
|
||||
};
|
||||
|
||||
typedef struct binder_devinfo {
|
||||
struct ofono_devinfo* di;
|
||||
RadioRequestGroup* g;
|
||||
GUtilIdleQueue* iq;
|
||||
char* log_prefix;
|
||||
char* imeisv;
|
||||
char* imei;
|
||||
} BinderDevInfo;
|
||||
|
||||
typedef struct binder_devinfo_callback_data {
|
||||
BinderDevInfo* self;
|
||||
ofono_devinfo_query_cb_t cb;
|
||||
gpointer data;
|
||||
} BinderDevInfoCbData;
|
||||
|
||||
#define DBG_(self,fmt,args...) DBG("%s" fmt, (self)->log_prefix, ##args)
|
||||
|
||||
static inline BinderDevInfo* binder_devinfo_get_data(struct ofono_devinfo* di)
|
||||
{ return ofono_devinfo_get_data(di); }
|
||||
|
||||
static
|
||||
BinderDevInfoCbData*
|
||||
binder_devinfo_callback_data_new(
|
||||
BinderDevInfo* self,
|
||||
ofono_devinfo_query_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
BinderDevInfoCbData* cbd = g_slice_new0(BinderDevInfoCbData);
|
||||
|
||||
cbd->self = self;
|
||||
cbd->cb = cb;
|
||||
cbd->data = data;
|
||||
return cbd;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_devinfo_callback_data_free(
|
||||
gpointer cbd)
|
||||
{
|
||||
g_slice_free(BinderDevInfoCbData, cbd);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_devinfo_query_unsupported(
|
||||
struct ofono_devinfo* di,
|
||||
ofono_devinfo_query_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
struct ofono_error error;
|
||||
|
||||
cb(binder_error_failure(&error), "", data);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_devinfo_query_revision_ok(
|
||||
const BinderDevInfoCbData* cbd,
|
||||
const GBinderReader* args)
|
||||
{
|
||||
struct ofono_error err;
|
||||
GBinderReader reader;
|
||||
const char* res;
|
||||
|
||||
/* getBasebandVersionResponse(RadioResponseInfo, string version); */
|
||||
gbinder_reader_copy(&reader, args);
|
||||
res = gbinder_reader_read_hidl_string_c(&reader);
|
||||
DBG_(cbd->self, "%s", res);
|
||||
cbd->cb(binder_error_ok(&err), res ? res : "", cbd->data);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_devinfo_query_revision_cb(
|
||||
RadioRequest* req,
|
||||
RADIO_TX_STATUS status,
|
||||
RADIO_RESP resp,
|
||||
RADIO_ERROR error,
|
||||
const GBinderReader* args,
|
||||
void* user_data)
|
||||
{
|
||||
struct ofono_error err;
|
||||
const BinderDevInfoCbData* cbd = user_data;
|
||||
|
||||
if (status == RADIO_TX_STATUS_OK) {
|
||||
if (resp == RADIO_RESP_GET_BASEBAND_VERSION) {
|
||||
if (error == RADIO_ERROR_NONE) {
|
||||
binder_devinfo_query_revision_ok(cbd, args);
|
||||
return;
|
||||
} else {
|
||||
ofono_error("getBasebandVersion error %d", error);
|
||||
}
|
||||
} else {
|
||||
ofono_error("Unexpected getBasebandVersion response %d", resp);
|
||||
}
|
||||
}
|
||||
cbd->cb(binder_error_failure(&err), NULL, cbd->data);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_devinfo_query_revision(
|
||||
struct ofono_devinfo* di,
|
||||
ofono_devinfo_query_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
BinderDevInfo* self = binder_devinfo_get_data(di);
|
||||
RadioRequest* req = radio_request_new2(self->g,
|
||||
RADIO_REQ_GET_BASEBAND_VERSION, NULL,
|
||||
binder_devinfo_query_revision_cb,
|
||||
binder_devinfo_callback_data_free,
|
||||
binder_devinfo_callback_data_new(self, cb, data));
|
||||
|
||||
DBG_(self, "");
|
||||
radio_request_submit(req);
|
||||
radio_request_unref(req);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_devinfo_query_serial_cb(
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderDevInfoCbData* cbd = user_data;
|
||||
BinderDevInfo* self = cbd->self;
|
||||
struct ofono_error error;
|
||||
|
||||
DBG_(self, "%s", self->imei);
|
||||
cbd->cb(binder_error_ok(&error), self->imei, cbd->data);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_devinfo_query_svn_cb(
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderDevInfoCbData* cbd = user_data;
|
||||
BinderDevInfo* self = cbd->self;
|
||||
struct ofono_error error;
|
||||
|
||||
DBG_(self, "%s", self->imeisv);
|
||||
if (self->imeisv && self->imeisv[0]) {
|
||||
cbd->cb(binder_error_ok(&error), self->imeisv, cbd->data);
|
||||
} else {
|
||||
cbd->cb(binder_error_failure(&error), "", cbd->data);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_devinfo_query(
|
||||
BinderDevInfo* self,
|
||||
enum binder_devinfo_cb_tag tag,
|
||||
GUtilIdleFunc fn,
|
||||
ofono_devinfo_query_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
GVERIFY_FALSE(gutil_idle_queue_cancel_tag(self->iq, tag));
|
||||
gutil_idle_queue_add_tag_full(self->iq, tag, fn,
|
||||
binder_devinfo_callback_data_new(self, cb, data),
|
||||
binder_devinfo_callback_data_free);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_devinfo_query_serial(
|
||||
struct ofono_devinfo* devinfo,
|
||||
ofono_devinfo_query_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
BinderDevInfo* self = binder_devinfo_get_data(devinfo);
|
||||
|
||||
DBG_(self, "");
|
||||
binder_devinfo_query(self, DEVINFO_QUERY_SERIAL,
|
||||
binder_devinfo_query_serial_cb, cb, data);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_devinfo_query_svn(
|
||||
struct ofono_devinfo* devinfo,
|
||||
ofono_devinfo_query_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
BinderDevInfo* self = binder_devinfo_get_data(devinfo);
|
||||
|
||||
DBG_(self, "");
|
||||
binder_devinfo_query(self, DEVINFO_QUERY_SVN,
|
||||
binder_devinfo_query_svn_cb, cb, data);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_devinfo_register(
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderDevInfo* self = user_data;
|
||||
|
||||
DBG_(self, "");
|
||||
ofono_devinfo_register(self->di);
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
binder_devinfo_probe(
|
||||
struct ofono_devinfo* di,
|
||||
unsigned int vendor,
|
||||
void* data)
|
||||
{
|
||||
BinderModem* modem = binder_modem_get_data(data);
|
||||
BinderDevInfo* self = g_new0(BinderDevInfo, 1);
|
||||
|
||||
self->log_prefix = binder_dup_prefix(modem->log_prefix);
|
||||
|
||||
DBG_(self, "%s", modem->imei);
|
||||
self->g = radio_request_group_new(modem->client);
|
||||
self->di = di;
|
||||
self->imeisv = g_strdup(modem->imeisv);
|
||||
self->imei = g_strdup(modem->imei);
|
||||
self->iq = gutil_idle_queue_new();
|
||||
gutil_idle_queue_add(self->iq, binder_devinfo_register, self);
|
||||
ofono_devinfo_set_data(di, self);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_devinfo_remove(
|
||||
struct ofono_devinfo* di)
|
||||
{
|
||||
BinderDevInfo* self = binder_devinfo_get_data(di);
|
||||
|
||||
DBG_(self, "");
|
||||
ofono_devinfo_set_data(di, NULL);
|
||||
radio_request_group_cancel(self->g);
|
||||
radio_request_group_unref(self->g);
|
||||
gutil_idle_queue_cancel_all(self->iq);
|
||||
gutil_idle_queue_unref(self->iq);
|
||||
g_free(self->log_prefix);
|
||||
g_free(self->imeisv);
|
||||
g_free(self->imei);
|
||||
g_free(self);
|
||||
}
|
||||
|
||||
/*==========================================================================*
|
||||
* API
|
||||
*==========================================================================*/
|
||||
|
||||
static const struct ofono_devinfo_driver binder_devinfo_driver = {
|
||||
.name = BINDER_DRIVER,
|
||||
.probe = binder_devinfo_probe,
|
||||
.remove = binder_devinfo_remove,
|
||||
/* query_revision won't be called if query_model is missing */
|
||||
.query_model = binder_devinfo_query_unsupported,
|
||||
.query_revision = binder_devinfo_query_revision,
|
||||
.query_serial = binder_devinfo_query_serial,
|
||||
.query_svn = binder_devinfo_query_svn
|
||||
};
|
||||
|
||||
void
|
||||
binder_devinfo_init()
|
||||
{
|
||||
ofono_devinfo_driver_register(&binder_devinfo_driver);
|
||||
}
|
||||
|
||||
void
|
||||
binder_devinfo_cleanup()
|
||||
{
|
||||
ofono_devinfo_driver_unregister(&binder_devinfo_driver);
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
37
src/binder_devinfo.h
Normal file
37
src/binder_devinfo.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef BINDER_DEVINFO_H
|
||||
#define BINDER_DEVINFO_H
|
||||
|
||||
#include "binder_types.h"
|
||||
|
||||
void
|
||||
binder_devinfo_init(void)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_devinfo_cleanup(void)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
#endif /* BINDER_DEVINFO_H */
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
51
src/binder_devmon.c
Normal file
51
src/binder_devmon.c
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "binder_devmon.h"
|
||||
|
||||
BinderDevmonIo*
|
||||
binder_devmon_start_io(
|
||||
BinderDevmon* devmon,
|
||||
RadioClient* client,
|
||||
struct ofono_slot* slot)
|
||||
{
|
||||
return devmon ? devmon->start_io(devmon, client, slot) : NULL;
|
||||
}
|
||||
|
||||
void
|
||||
binder_devmon_io_free(
|
||||
BinderDevmonIo* io)
|
||||
{
|
||||
if (io) {
|
||||
io->free(io);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
binder_devmon_free(
|
||||
BinderDevmon* devmon)
|
||||
{
|
||||
if (devmon) {
|
||||
devmon->free(devmon);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
95
src/binder_devmon.h
Normal file
95
src/binder_devmon.h
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef BINDER_DEVMON_H
|
||||
#define BINDER_DEVMON_H
|
||||
|
||||
#include "binder_types.h"
|
||||
|
||||
#include <ofono/slot.h>
|
||||
|
||||
/*
|
||||
* Separate instance of BinderDevmon is created for each modem.
|
||||
* Device monitor is started after connection to the modem has
|
||||
* been established.
|
||||
*/
|
||||
|
||||
typedef struct binder_devmon_io BinderDevmonIo;
|
||||
struct binder_devmon_io {
|
||||
void (*free)(BinderDevmonIo* io);
|
||||
};
|
||||
|
||||
struct binder_devmon {
|
||||
void (*free)(BinderDevmon* devmon);
|
||||
BinderDevmonIo* (*start_io)(BinderDevmon* devmon, RadioClient* client,
|
||||
struct ofono_slot* slot);
|
||||
};
|
||||
|
||||
/*
|
||||
* This Device Monitor uses sendDeviceState() call to let the modem
|
||||
* choose the right power saving strategy. It basically mirrors the
|
||||
* logic of DeviceStateMonitor class in Android.
|
||||
*/
|
||||
BinderDevmon*
|
||||
binder_devmon_ds_new(
|
||||
const BinderSlotConfig* config)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
/*
|
||||
* This Device Monitor implementation controls network state updates
|
||||
* by calling setIndicationFilter().
|
||||
*/
|
||||
BinderDevmon*
|
||||
binder_devmon_if_new(
|
||||
const BinderSlotConfig* config)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
/*
|
||||
* This one combines several methods. Takes ownership of binder_devmon objects.
|
||||
*/
|
||||
BinderDevmon*
|
||||
binder_devmon_combine(
|
||||
BinderDevmon* devmon[],
|
||||
guint n)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
/* Utilities (NULL tolerant) */
|
||||
|
||||
BinderDevmonIo*
|
||||
binder_devmon_start_io(
|
||||
BinderDevmon* devmon,
|
||||
RadioClient* client,
|
||||
struct ofono_slot* slot)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_devmon_io_free(
|
||||
BinderDevmonIo* io)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_devmon_free(
|
||||
BinderDevmon* devmon)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
#endif /* BINDER_DEVMON_H */
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
117
src/binder_devmon_combine.c
Normal file
117
src/binder_devmon_combine.c
Normal file
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "binder_devmon.h"
|
||||
|
||||
#include <ofono/log.h>
|
||||
|
||||
#include <gutil_macros.h>
|
||||
|
||||
typedef struct binder_devmon_combine {
|
||||
BinderDevmon pub;
|
||||
BinderDevmon** impl;
|
||||
guint count;
|
||||
} DevMon;
|
||||
|
||||
typedef struct binder_devmon_combine_io {
|
||||
BinderDevmonIo pub;
|
||||
BinderDevmonIo** impl;
|
||||
guint count;
|
||||
} DevMonIo;
|
||||
|
||||
static inline DevMon* binder_devmon_combine_cast(BinderDevmon* devmon)
|
||||
{ return G_CAST(devmon, DevMon, pub); }
|
||||
|
||||
static inline DevMonIo* binder_devmon_combine_io_cast(BinderDevmonIo* io)
|
||||
{ return G_CAST(io, DevMonIo, pub); }
|
||||
|
||||
static
|
||||
void
|
||||
binder_devmon_combine_io_free(
|
||||
BinderDevmonIo* io)
|
||||
{
|
||||
guint i;
|
||||
DevMonIo* self = binder_devmon_combine_io_cast(io);
|
||||
|
||||
for (i = 0; i < self->count; i++) {
|
||||
binder_devmon_io_free(self->impl[i]);
|
||||
}
|
||||
g_free(self);
|
||||
}
|
||||
|
||||
static
|
||||
BinderDevmonIo*
|
||||
binder_devmon_combine_start_io(
|
||||
BinderDevmon* devmon,
|
||||
RadioClient* client,
|
||||
struct ofono_slot* slot)
|
||||
{
|
||||
guint i;
|
||||
DevMon* self = binder_devmon_combine_cast(devmon);
|
||||
DevMonIo* io = g_malloc0(sizeof(DevMonIo) +
|
||||
sizeof(BinderDevmonIo*) * self->count);
|
||||
|
||||
io->pub.free = binder_devmon_combine_io_free;
|
||||
io->impl = (BinderDevmonIo**)(io + 1);
|
||||
io->count = self->count;
|
||||
for (i = 0; i < io->count; i++) {
|
||||
io->impl[i] = binder_devmon_start_io(self->impl[i], client, slot);
|
||||
}
|
||||
return &io->pub;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_devmon_combine_free(
|
||||
BinderDevmon* dm)
|
||||
{
|
||||
DevMon* self = binder_devmon_combine_cast(dm);
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < self->count; i++) {
|
||||
binder_devmon_free(self->impl[i]);
|
||||
}
|
||||
g_free(self);
|
||||
}
|
||||
|
||||
/*==========================================================================*
|
||||
* API
|
||||
*==========================================================================*/
|
||||
|
||||
BinderDevmon*
|
||||
binder_devmon_combine(
|
||||
BinderDevmon* dm[],
|
||||
guint n)
|
||||
{
|
||||
guint i;
|
||||
DevMon* self = g_malloc0(sizeof(DevMon) + sizeof(BinderDevmon*) * n);
|
||||
|
||||
self->pub.free = binder_devmon_combine_free;
|
||||
self->pub.start_io = binder_devmon_combine_start_io;
|
||||
self->impl = (BinderDevmon**)(self + 1);
|
||||
self->count = n;
|
||||
for (i = 0; i < n; i++) {
|
||||
self->impl[i] = dm[i];
|
||||
}
|
||||
return &self->pub;
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
417
src/binder_devmon_ds.c
Normal file
417
src/binder_devmon_ds.c
Normal file
@@ -0,0 +1,417 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "binder_devmon.h"
|
||||
#include "binder_connman.h"
|
||||
#include "binder_log.h"
|
||||
|
||||
#include <ofono/log.h>
|
||||
|
||||
#include <mce_battery.h>
|
||||
#include <mce_charger.h>
|
||||
#include <mce_display.h>
|
||||
|
||||
#include <radio_client.h>
|
||||
#include <radio_request.h>
|
||||
|
||||
#include <gbinder_writer.h>
|
||||
|
||||
#include <gutil_macros.h>
|
||||
|
||||
enum binder_devmon_ds_battery_event {
|
||||
BATTERY_EVENT_VALID,
|
||||
BATTERY_EVENT_STATUS,
|
||||
BATTERY_EVENT_COUNT
|
||||
};
|
||||
|
||||
enum binder_devmon_ds_charger_event {
|
||||
CHARGER_EVENT_VALID,
|
||||
CHARGER_EVENT_STATE,
|
||||
CHARGER_EVENT_COUNT
|
||||
};
|
||||
|
||||
enum binder_devmon_ds_display_event {
|
||||
DISPLAY_EVENT_VALID,
|
||||
DISPLAY_EVENT_STATE,
|
||||
DISPLAY_EVENT_COUNT
|
||||
};
|
||||
|
||||
enum binder_devmon_ds_connman_event {
|
||||
CONNMAN_EVENT_VALID,
|
||||
CONNMAN_EVENT_TETHERING,
|
||||
CONNMAN_EVENT_COUNT
|
||||
};
|
||||
|
||||
typedef struct binder_devmon_ds {
|
||||
BinderDevmon pub;
|
||||
BinderConnman* connman;
|
||||
MceBattery* battery;
|
||||
MceCharger* charger;
|
||||
MceDisplay* display;
|
||||
int cell_info_interval_short_ms;
|
||||
int cell_info_interval_long_ms;
|
||||
} DevMon;
|
||||
|
||||
typedef struct binder_devmon_ds_io {
|
||||
BinderDevmonIo pub;
|
||||
BinderConnman* connman;
|
||||
struct ofono_slot* slot;
|
||||
MceBattery* battery;
|
||||
MceCharger* charger;
|
||||
MceDisplay* display;
|
||||
RadioClient* client;
|
||||
RadioRequest* low_data_req;
|
||||
RadioRequest* charging_req;
|
||||
gboolean low_data;
|
||||
gboolean charging;
|
||||
gboolean low_data_supported;
|
||||
gboolean charging_supported;
|
||||
gulong connman_event_id[CONNMAN_EVENT_COUNT];
|
||||
gulong battery_event_id[BATTERY_EVENT_COUNT];
|
||||
gulong charger_event_id[CHARGER_EVENT_COUNT];
|
||||
gulong display_event_id[DISPLAY_EVENT_COUNT];
|
||||
int cell_info_interval_short_ms;
|
||||
int cell_info_interval_long_ms;
|
||||
} DevMonIo;
|
||||
|
||||
#define DBG_(self,fmt,args...) \
|
||||
DBG("%s: " fmt, radio_client_slot((self)->client), ##args)
|
||||
|
||||
static inline DevMon* binder_devmon_ds_cast(BinderDevmon* pub)
|
||||
{ return G_CAST(pub, DevMon, pub); }
|
||||
|
||||
static inline DevMonIo* binder_devmon_ds_io_cast(BinderDevmonIo* pub)
|
||||
{ return G_CAST(pub, DevMonIo, pub); }
|
||||
|
||||
static inline gboolean binder_devmon_ds_tethering_on(BinderConnman* connman)
|
||||
{ return connman->valid && connman->tethering; }
|
||||
|
||||
static inline gboolean binder_devmon_ds_battery_ok(MceBattery* battery)
|
||||
{ return battery->valid && battery->status >= MCE_BATTERY_OK; }
|
||||
|
||||
static inline gboolean binder_devmon_ds_charging(MceCharger* charger)
|
||||
{ return charger->valid && charger->state == MCE_CHARGER_ON; }
|
||||
|
||||
static inline gboolean binder_devmon_ds_display_on(MceDisplay* display)
|
||||
{ return display->valid && display->state != MCE_DISPLAY_STATE_OFF; }
|
||||
|
||||
static
|
||||
void
|
||||
binder_devmon_ds_io_low_data_state_sent(
|
||||
RadioRequest* req,
|
||||
RADIO_TX_STATUS status,
|
||||
RADIO_RESP resp,
|
||||
RADIO_ERROR error,
|
||||
const GBinderReader* args,
|
||||
gpointer user_data)
|
||||
{
|
||||
DevMonIo* self = user_data;
|
||||
|
||||
GASSERT(self->low_data_req == req);
|
||||
radio_request_unref(self->low_data_req);
|
||||
self->low_data_req = NULL;
|
||||
|
||||
if (status == RADIO_TX_STATUS_OK) {
|
||||
if (resp == RADIO_RESP_SEND_DEVICE_STATE) {
|
||||
if (error == RADIO_ERROR_REQUEST_NOT_SUPPORTED) {
|
||||
DBG_(self, "LOW_DATA_EXPECTED state is not supported");
|
||||
self->low_data_supported = FALSE;
|
||||
}
|
||||
} else {
|
||||
ofono_error("Unexpected sendDeviceState response %d", resp);
|
||||
self->low_data_supported = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_devmon_ds_io_charging_state_sent(
|
||||
RadioRequest* req,
|
||||
RADIO_TX_STATUS status,
|
||||
RADIO_RESP resp,
|
||||
RADIO_ERROR error,
|
||||
const GBinderReader* args,
|
||||
gpointer user_data)
|
||||
{
|
||||
DevMonIo* self = user_data;
|
||||
|
||||
GASSERT(self->charging_req == req);
|
||||
radio_request_unref(self->charging_req);
|
||||
self->charging_req = NULL;
|
||||
|
||||
if (status == RADIO_TX_STATUS_OK) {
|
||||
if (resp == RADIO_RESP_SEND_DEVICE_STATE) {
|
||||
if (error == RADIO_ERROR_REQUEST_NOT_SUPPORTED) {
|
||||
DBG_(self, "CHARGING state is not supported");
|
||||
self->charging_supported = FALSE;
|
||||
}
|
||||
} else {
|
||||
ofono_error("Unexpected sendDeviceState response %d", resp);
|
||||
self->charging_supported = FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
RadioRequest*
|
||||
binder_devmon_ds_io_send_device_state(
|
||||
DevMonIo* self,
|
||||
RADIO_DEVICE_STATE type,
|
||||
gboolean state,
|
||||
RadioRequestCompleteFunc callback)
|
||||
{
|
||||
GBinderWriter writer;
|
||||
RadioRequest* req = radio_request_new(self->client,
|
||||
RADIO_REQ_SEND_DEVICE_STATE, &writer, callback, NULL, self);
|
||||
|
||||
/* sendDeviceState(int32_t serial, DeviceStateType type, bool state); */
|
||||
gbinder_writer_append_int32(&writer, type);
|
||||
gbinder_writer_append_bool(&writer, state);
|
||||
if (radio_request_submit(req)) {
|
||||
return req;
|
||||
} else {
|
||||
radio_request_unref(req);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_devmon_ds_io_update_charging(
|
||||
DevMonIo* self)
|
||||
{
|
||||
const gboolean charging = binder_devmon_ds_charging(self->charger);
|
||||
|
||||
if (self->charging != charging) {
|
||||
self->charging = charging;
|
||||
DBG_(self, "Charging %s", charging ? "on" : "off");
|
||||
if (self->charging_supported) {
|
||||
radio_request_drop(self->charging_req);
|
||||
self->charging_req = binder_devmon_ds_io_send_device_state(self,
|
||||
RADIO_DEVICE_STATE_CHARGING_STATE, charging,
|
||||
binder_devmon_ds_io_charging_state_sent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_devmon_ds_io_update_low_data(
|
||||
DevMonIo* self)
|
||||
{
|
||||
const gboolean low_data =
|
||||
!binder_devmon_ds_tethering_on(self->connman) &&
|
||||
!binder_devmon_ds_charging(self->charger) &&
|
||||
!binder_devmon_ds_display_on(self->display);
|
||||
|
||||
if (self->low_data != low_data) {
|
||||
self->low_data = low_data;
|
||||
DBG_(self, "Low data is%s expected", low_data ? "" : " not");
|
||||
if (self->low_data_supported) {
|
||||
radio_request_drop(self->low_data_req);
|
||||
self->low_data_req = binder_devmon_ds_io_send_device_state(self,
|
||||
RADIO_DEVICE_STATE_LOW_DATA_EXPECTED, low_data,
|
||||
binder_devmon_ds_io_low_data_state_sent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_devmon_ds_io_set_cell_info_update_interval(
|
||||
DevMonIo* self)
|
||||
{
|
||||
ofono_slot_set_cell_info_update_interval(self->slot, self,
|
||||
(binder_devmon_ds_display_on(self->display) &&
|
||||
(binder_devmon_ds_charging(self->charger) ||
|
||||
binder_devmon_ds_battery_ok(self->battery))) ?
|
||||
self->cell_info_interval_short_ms :
|
||||
self->cell_info_interval_long_ms);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_devmon_ds_io_connman_cb(
|
||||
BinderConnman* connman,
|
||||
BINDER_CONNMAN_PROPERTY property,
|
||||
void* user_data)
|
||||
{
|
||||
binder_devmon_ds_io_update_low_data((DevMonIo*)user_data);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_devmon_ds_io_battery_cb(
|
||||
MceBattery* battery,
|
||||
void* user_data)
|
||||
{
|
||||
binder_devmon_ds_io_set_cell_info_update_interval((DevMonIo*)user_data);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_devmon_ds_io_display_cb(
|
||||
MceDisplay* display,
|
||||
void* user_data)
|
||||
{
|
||||
DevMonIo* self = user_data;
|
||||
|
||||
binder_devmon_ds_io_update_low_data(self);
|
||||
binder_devmon_ds_io_set_cell_info_update_interval(self);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_devmon_ds_io_charger_cb(
|
||||
MceCharger* charger,
|
||||
void* user_data)
|
||||
{
|
||||
DevMonIo* self = user_data;
|
||||
|
||||
binder_devmon_ds_io_update_low_data(self);
|
||||
binder_devmon_ds_io_update_charging(self);
|
||||
binder_devmon_ds_io_set_cell_info_update_interval(self);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_devmon_ds_io_free(
|
||||
BinderDevmonIo* io)
|
||||
{
|
||||
DevMonIo* self = binder_devmon_ds_io_cast(io);
|
||||
|
||||
binder_connman_remove_all_handlers(self->connman, self->connman_event_id);
|
||||
binder_connman_unref(self->connman);
|
||||
|
||||
mce_battery_remove_all_handlers(self->battery, self->battery_event_id);
|
||||
mce_battery_unref(self->battery);
|
||||
|
||||
mce_charger_remove_all_handlers(self->charger, self->charger_event_id);
|
||||
mce_charger_unref(self->charger);
|
||||
|
||||
mce_display_remove_all_handlers(self->display, self->display_event_id);
|
||||
mce_display_unref(self->display);
|
||||
|
||||
radio_request_drop(self->low_data_req);
|
||||
radio_request_drop(self->charging_req);
|
||||
radio_client_unref(self->client);
|
||||
|
||||
ofono_slot_drop_cell_info_requests(self->slot, self);
|
||||
ofono_slot_unref(self->slot);
|
||||
g_free(self);
|
||||
}
|
||||
|
||||
static
|
||||
BinderDevmonIo*
|
||||
binder_devmon_ds_start_io(
|
||||
BinderDevmon* devmon,
|
||||
RadioClient* client,
|
||||
struct ofono_slot* slot)
|
||||
{
|
||||
DevMon* ds = binder_devmon_ds_cast(devmon);
|
||||
DevMonIo* self = g_new0(DevMonIo, 1);
|
||||
|
||||
self->pub.free = binder_devmon_ds_io_free;
|
||||
self->low_data_supported = TRUE;
|
||||
self->charging_supported = TRUE;
|
||||
self->client = radio_client_ref(client);
|
||||
self->slot = ofono_slot_ref(slot);
|
||||
|
||||
self->connman = binder_connman_ref(ds->connman);
|
||||
self->connman_event_id[CONNMAN_EVENT_VALID] =
|
||||
binder_connman_add_property_changed_handler(self->connman,
|
||||
BINDER_CONNMAN_PROPERTY_VALID,
|
||||
binder_devmon_ds_io_connman_cb, self);
|
||||
self->connman_event_id[CONNMAN_EVENT_TETHERING] =
|
||||
binder_connman_add_property_changed_handler(self->connman,
|
||||
BINDER_CONNMAN_PROPERTY_TETHERING,
|
||||
binder_devmon_ds_io_connman_cb, self);
|
||||
|
||||
self->battery = mce_battery_ref(ds->battery);
|
||||
self->battery_event_id[BATTERY_EVENT_VALID] =
|
||||
mce_battery_add_valid_changed_handler(self->battery,
|
||||
binder_devmon_ds_io_battery_cb, self);
|
||||
self->battery_event_id[BATTERY_EVENT_STATUS] =
|
||||
mce_battery_add_status_changed_handler(self->battery,
|
||||
binder_devmon_ds_io_battery_cb, self);
|
||||
|
||||
self->charger = mce_charger_ref(ds->charger);
|
||||
self->charger_event_id[CHARGER_EVENT_VALID] =
|
||||
mce_charger_add_valid_changed_handler(self->charger,
|
||||
binder_devmon_ds_io_charger_cb, self);
|
||||
self->charger_event_id[CHARGER_EVENT_STATE] =
|
||||
mce_charger_add_state_changed_handler(self->charger,
|
||||
binder_devmon_ds_io_charger_cb, self);
|
||||
|
||||
self->display = mce_display_ref(ds->display);
|
||||
self->display_event_id[DISPLAY_EVENT_VALID] =
|
||||
mce_display_add_valid_changed_handler(self->display,
|
||||
binder_devmon_ds_io_display_cb, self);
|
||||
self->display_event_id[DISPLAY_EVENT_STATE] =
|
||||
mce_display_add_state_changed_handler(self->display,
|
||||
binder_devmon_ds_io_display_cb, self);
|
||||
|
||||
self->cell_info_interval_short_ms = ds->cell_info_interval_short_ms;
|
||||
self->cell_info_interval_long_ms = ds->cell_info_interval_long_ms;
|
||||
|
||||
binder_devmon_ds_io_update_low_data(self);
|
||||
binder_devmon_ds_io_update_charging(self);
|
||||
binder_devmon_ds_io_set_cell_info_update_interval(self);
|
||||
return &self->pub;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_devmon_ds_free(
|
||||
BinderDevmon* devmon)
|
||||
{
|
||||
DevMon* self = binder_devmon_ds_cast(devmon);
|
||||
|
||||
binder_connman_unref(self->connman);
|
||||
mce_battery_unref(self->battery);
|
||||
mce_charger_unref(self->charger);
|
||||
mce_display_unref(self->display);
|
||||
g_free(self);
|
||||
}
|
||||
|
||||
/*==========================================================================*
|
||||
* API
|
||||
*==========================================================================*/
|
||||
|
||||
BinderDevmon*
|
||||
binder_devmon_ds_new(
|
||||
const BinderSlotConfig* config)
|
||||
{
|
||||
DevMon* self = g_new0(DevMon, 1);
|
||||
|
||||
self->pub.free = binder_devmon_ds_free;
|
||||
self->pub.start_io = binder_devmon_ds_start_io;
|
||||
self->connman = binder_connman_new();
|
||||
self->battery = mce_battery_new();
|
||||
self->charger = mce_charger_new();
|
||||
self->display = mce_display_new();
|
||||
self->cell_info_interval_short_ms = config->cell_info_interval_short_ms;
|
||||
self->cell_info_interval_long_ms = config->cell_info_interval_long_ms;
|
||||
return &self->pub;
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
322
src/binder_devmon_if.c
Normal file
322
src/binder_devmon_if.c
Normal file
@@ -0,0 +1,322 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "binder_devmon.h"
|
||||
#include "binder_log.h"
|
||||
|
||||
#include <ofono/log.h>
|
||||
|
||||
#include <mce_battery.h>
|
||||
#include <mce_charger.h>
|
||||
#include <mce_display.h>
|
||||
|
||||
#include <radio_client.h>
|
||||
#include <radio_request.h>
|
||||
|
||||
#include <gbinder_writer.h>
|
||||
|
||||
#include <gutil_macros.h>
|
||||
|
||||
enum binder_devmon_if_battery_event {
|
||||
BATTERY_EVENT_VALID,
|
||||
BATTERY_EVENT_STATUS,
|
||||
BATTERY_EVENT_COUNT
|
||||
};
|
||||
|
||||
enum binder_devmon_if_charger_event {
|
||||
CHARGER_EVENT_VALID,
|
||||
CHARGER_EVENT_STATE,
|
||||
CHARGER_EVENT_COUNT
|
||||
};
|
||||
|
||||
enum binder_devmon_if_display_event {
|
||||
DISPLAY_EVENT_VALID,
|
||||
DISPLAY_EVENT_STATE,
|
||||
DISPLAY_EVENT_COUNT
|
||||
};
|
||||
|
||||
typedef struct binder_devmon_if {
|
||||
BinderDevmon pub;
|
||||
MceBattery* battery;
|
||||
MceCharger* charger;
|
||||
MceDisplay* display;
|
||||
int cell_info_interval_short_ms;
|
||||
int cell_info_interval_long_ms;
|
||||
} DevMon;
|
||||
|
||||
typedef struct binder_devmon_if_io {
|
||||
BinderDevmonIo pub;
|
||||
struct ofono_slot* slot;
|
||||
MceBattery* battery;
|
||||
MceCharger* charger;
|
||||
MceDisplay* display;
|
||||
RadioClient* client;
|
||||
RadioRequest* req;
|
||||
gboolean display_on;
|
||||
gboolean ind_filter_supported;
|
||||
gulong battery_event_id[BATTERY_EVENT_COUNT];
|
||||
gulong charger_event_id[CHARGER_EVENT_COUNT];
|
||||
gulong display_event_id[DISPLAY_EVENT_COUNT];
|
||||
int cell_info_interval_short_ms;
|
||||
int cell_info_interval_long_ms;
|
||||
} DevMonIo;
|
||||
|
||||
#define DBG_(self,fmt,args...) \
|
||||
DBG("%s: " fmt, radio_client_slot((self)->client), ##args)
|
||||
|
||||
inline static DevMon* binder_devmon_if_cast(BinderDevmon* pub)
|
||||
{ return G_CAST(pub, DevMon, pub); }
|
||||
|
||||
inline static DevMonIo* binder_devmon_if_io_cast(BinderDevmonIo* pub)
|
||||
{ return G_CAST(pub, DevMonIo, pub); }
|
||||
|
||||
static inline gboolean binder_devmon_if_battery_ok(MceBattery* battery)
|
||||
{ return battery->valid && battery->status >= MCE_BATTERY_OK; }
|
||||
|
||||
static inline gboolean binder_devmon_if_charging(MceCharger* charger)
|
||||
{ return charger->valid && charger->state == MCE_CHARGER_ON; }
|
||||
|
||||
static gboolean binder_devmon_if_display_on(MceDisplay* display)
|
||||
{ return display->valid && display->state != MCE_DISPLAY_STATE_OFF; }
|
||||
|
||||
static
|
||||
void
|
||||
binder_devmon_if_io_indication_filter_sent(
|
||||
RadioRequest* req,
|
||||
RADIO_TX_STATUS status,
|
||||
RADIO_RESP resp,
|
||||
RADIO_ERROR error,
|
||||
const GBinderReader* args,
|
||||
gpointer user_data)
|
||||
{
|
||||
DevMonIo* self = user_data;
|
||||
|
||||
GASSERT(self->req == req);
|
||||
radio_request_unref(self->req);
|
||||
self->req = NULL;
|
||||
|
||||
if (status == RADIO_TX_STATUS_OK) {
|
||||
if (resp == RADIO_RESP_SET_INDICATION_FILTER) {
|
||||
if (error == RADIO_ERROR_REQUEST_NOT_SUPPORTED) {
|
||||
/* This is a permanent failure */
|
||||
DBG_(self, "Indication response filter is not supported");
|
||||
self->ind_filter_supported = FALSE;
|
||||
}
|
||||
} else {
|
||||
ofono_error("Unexpected setIndicationFilter response %d", resp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_devmon_if_io_set_indication_filter(
|
||||
DevMonIo* self)
|
||||
{
|
||||
if (self->ind_filter_supported) {
|
||||
GBinderWriter args;
|
||||
RADIO_REQ code;
|
||||
gint32 value;
|
||||
|
||||
/*
|
||||
* Both requests take the same args:
|
||||
*
|
||||
* setIndicationFilter(serial, bitfield<IndicationFilter>)
|
||||
* setIndicationFilter_1_2(serial, bitfield<IndicationFilter>)
|
||||
*
|
||||
* and both produce IRadioResponse.setIndicationFilterResponse()
|
||||
*
|
||||
* However setIndicationFilter_1_2 comments says "If unset, defaults
|
||||
* to @1.2::IndicationFilter:ALL" and it's unclear what "unset" means
|
||||
* wrt a bitmask. How is "unset" different from NONE which is zero.
|
||||
* To be on the safe side, let's always set the most innocently
|
||||
* looking bit which I think is DATA_CALL_DORMANCY.
|
||||
*/
|
||||
if (radio_client_interface(self->client) < RADIO_INTERFACE_1_2) {
|
||||
code = RADIO_REQ_SET_INDICATION_FILTER;
|
||||
value = self->display_on ? RADIO_IND_FILTER_ALL :
|
||||
RADIO_IND_FILTER_DATA_CALL_DORMANCY;
|
||||
} else {
|
||||
code = RADIO_REQ_SET_INDICATION_FILTER_1_2;
|
||||
value = self->display_on ? RADIO_IND_FILTER_ALL_1_2 :
|
||||
RADIO_IND_FILTER_DATA_CALL_DORMANCY;
|
||||
}
|
||||
|
||||
radio_request_drop(self->req);
|
||||
self->req = radio_request_new(self->client, code, &args,
|
||||
binder_devmon_if_io_indication_filter_sent, NULL, self);
|
||||
gbinder_writer_append_int32(&args, value);
|
||||
DBG_(self, "Setting indication filter: 0x%02x", value);
|
||||
radio_request_submit(self->req);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_devmon_if_io_set_cell_info_update_interval(
|
||||
DevMonIo* self)
|
||||
{
|
||||
ofono_slot_set_cell_info_update_interval(self->slot, self,
|
||||
(self->display_on && (binder_devmon_if_charging(self->charger) ||
|
||||
binder_devmon_if_battery_ok(self->battery))) ?
|
||||
self->cell_info_interval_short_ms :
|
||||
self->cell_info_interval_long_ms);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_devmon_if_io_battery_cb(
|
||||
MceBattery* battery,
|
||||
void* user_data)
|
||||
{
|
||||
binder_devmon_if_io_set_cell_info_update_interval((DevMonIo*)user_data);
|
||||
}
|
||||
|
||||
static
|
||||
void binder_devmon_if_io_charger_cb(
|
||||
MceCharger* charger,
|
||||
void* user_data)
|
||||
{
|
||||
binder_devmon_if_io_set_cell_info_update_interval((DevMonIo*)user_data);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_devmon_if_io_display_cb(
|
||||
MceDisplay* display,
|
||||
void* user_data)
|
||||
{
|
||||
DevMonIo* self = user_data;
|
||||
const gboolean display_on = binder_devmon_if_display_on(display);
|
||||
|
||||
if (self->display_on != display_on) {
|
||||
self->display_on = display_on;
|
||||
binder_devmon_if_io_set_indication_filter(self);
|
||||
binder_devmon_if_io_set_cell_info_update_interval(self);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_devmon_if_io_free(
|
||||
BinderDevmonIo* io)
|
||||
{
|
||||
DevMonIo* self = binder_devmon_if_io_cast(io);
|
||||
|
||||
mce_battery_remove_all_handlers(self->battery, self->battery_event_id);
|
||||
mce_battery_unref(self->battery);
|
||||
|
||||
mce_charger_remove_all_handlers(self->charger, self->charger_event_id);
|
||||
mce_charger_unref(self->charger);
|
||||
|
||||
mce_display_remove_all_handlers(self->display, self->display_event_id);
|
||||
mce_display_unref(self->display);
|
||||
|
||||
radio_request_drop(self->req);
|
||||
radio_client_unref(self->client);
|
||||
|
||||
ofono_slot_drop_cell_info_requests(self->slot, self);
|
||||
ofono_slot_unref(self->slot);
|
||||
g_free(self);
|
||||
}
|
||||
|
||||
static
|
||||
BinderDevmonIo*
|
||||
binder_devmon_if_start_io(
|
||||
BinderDevmon* devmon,
|
||||
RadioClient* client,
|
||||
struct ofono_slot* slot)
|
||||
{
|
||||
DevMon* impl = binder_devmon_if_cast(devmon);
|
||||
DevMonIo* self = g_new0(DevMonIo, 1);
|
||||
|
||||
self->pub.free = binder_devmon_if_io_free;
|
||||
self->ind_filter_supported = TRUE;
|
||||
self->client = radio_client_ref(client);
|
||||
self->slot = ofono_slot_ref(slot);
|
||||
|
||||
self->battery = mce_battery_ref(impl->battery);
|
||||
self->battery_event_id[BATTERY_EVENT_VALID] =
|
||||
mce_battery_add_valid_changed_handler(self->battery,
|
||||
binder_devmon_if_io_battery_cb, self);
|
||||
self->battery_event_id[BATTERY_EVENT_STATUS] =
|
||||
mce_battery_add_status_changed_handler(self->battery,
|
||||
binder_devmon_if_io_battery_cb, self);
|
||||
|
||||
self->charger = mce_charger_ref(impl->charger);
|
||||
self->charger_event_id[CHARGER_EVENT_VALID] =
|
||||
mce_charger_add_valid_changed_handler(self->charger,
|
||||
binder_devmon_if_io_charger_cb, self);
|
||||
self->charger_event_id[CHARGER_EVENT_STATE] =
|
||||
mce_charger_add_state_changed_handler(self->charger,
|
||||
binder_devmon_if_io_charger_cb, self);
|
||||
|
||||
self->display = mce_display_ref(impl->display);
|
||||
self->display_on = binder_devmon_if_display_on(self->display);
|
||||
self->display_event_id[DISPLAY_EVENT_VALID] =
|
||||
mce_display_add_valid_changed_handler(self->display,
|
||||
binder_devmon_if_io_display_cb, self);
|
||||
self->display_event_id[DISPLAY_EVENT_STATE] =
|
||||
mce_display_add_state_changed_handler(self->display,
|
||||
binder_devmon_if_io_display_cb, self);
|
||||
|
||||
self->cell_info_interval_short_ms = impl->cell_info_interval_short_ms;
|
||||
self->cell_info_interval_long_ms = impl->cell_info_interval_long_ms;
|
||||
|
||||
binder_devmon_if_io_set_indication_filter(self);
|
||||
binder_devmon_if_io_set_cell_info_update_interval(self);
|
||||
return &self->pub;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_devmon_if_free(
|
||||
BinderDevmon* devmon)
|
||||
{
|
||||
DevMon* self = binder_devmon_if_cast(devmon);
|
||||
|
||||
mce_battery_unref(self->battery);
|
||||
mce_charger_unref(self->charger);
|
||||
mce_display_unref(self->display);
|
||||
g_free(self);
|
||||
}
|
||||
|
||||
/*==========================================================================*
|
||||
* API
|
||||
*==========================================================================*/
|
||||
|
||||
BinderDevmon*
|
||||
binder_devmon_if_new(
|
||||
const BinderSlotConfig* config)
|
||||
{
|
||||
DevMon* self = g_new0(DevMon, 1);
|
||||
|
||||
self->pub.free = binder_devmon_if_free;
|
||||
self->pub.start_io = binder_devmon_if_start_io;
|
||||
self->battery = mce_battery_new();
|
||||
self->charger = mce_charger_new();
|
||||
self->display = mce_display_new();
|
||||
self->cell_info_interval_short_ms = config->cell_info_interval_short_ms;
|
||||
self->cell_info_interval_long_ms = config->cell_info_interval_long_ms;
|
||||
return &self->pub;
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
350
src/binder_gprs.c
Normal file
350
src/binder_gprs.c
Normal file
@@ -0,0 +1,350 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "binder_data.h"
|
||||
#include "binder_gprs.h"
|
||||
#include "binder_modem.h"
|
||||
#include "binder_log.h"
|
||||
#include "binder_netreg.h"
|
||||
#include "binder_network.h"
|
||||
#include "binder_util.h"
|
||||
|
||||
#include <ofono/gprs.h>
|
||||
#include <ofono/log.h>
|
||||
#include <ofono/misc.h>
|
||||
#include <ofono/netreg.h>
|
||||
#include <ofono/watch.h>
|
||||
|
||||
enum binder_gprs_network_events {
|
||||
NETWORK_EVENT_DATA_STATE,
|
||||
NETWORK_EVENT_MAX_DATA_CALLS,
|
||||
NETWORK_EVENT_COUNT
|
||||
};
|
||||
|
||||
typedef struct binder_gprs {
|
||||
struct ofono_gprs* gprs;
|
||||
struct ofono_watch* watch;
|
||||
BinderData* data;
|
||||
BinderNetwork* network;
|
||||
enum ofono_netreg_status reg_status;
|
||||
gboolean attached;
|
||||
gulong network_event_id[NETWORK_EVENT_COUNT];
|
||||
gulong data_event_id;
|
||||
guint set_attached_id;
|
||||
guint init_id;
|
||||
char* log_prefix;
|
||||
} BinderGprs;
|
||||
|
||||
typedef struct binder_gprs_cbd {
|
||||
BinderGprs* self;
|
||||
ofono_gprs_cb_t cb;
|
||||
gpointer data;
|
||||
} BinderGprsCbData;
|
||||
|
||||
#define DBG_(self,fmt,args...) DBG("%s" fmt, (self)->log_prefix, ##args)
|
||||
|
||||
static BinderGprs* binder_gprs_get_data(struct ofono_gprs* ofono)
|
||||
{ return ofono ? ofono_gprs_get_data(ofono) : NULL; }
|
||||
|
||||
static
|
||||
BinderGprsCbData*
|
||||
binder_gprs_cbd_new(
|
||||
BinderGprs* self,
|
||||
ofono_gprs_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
BinderGprsCbData* cbd = g_slice_new0(BinderGprsCbData);
|
||||
|
||||
cbd->self = self;
|
||||
cbd->cb = cb;
|
||||
cbd->data = data;
|
||||
return cbd;
|
||||
}
|
||||
static
|
||||
void
|
||||
binder_gprs_cbd_free(
|
||||
gpointer cbd)
|
||||
{
|
||||
g_slice_free(BinderGprsCbData, cbd);
|
||||
}
|
||||
|
||||
static
|
||||
enum ofono_netreg_status
|
||||
binder_gprs_fix_registration_status(
|
||||
BinderGprs* self,
|
||||
enum ofono_netreg_status status)
|
||||
{
|
||||
if (!binder_data_allowed(self->data)) {
|
||||
return OFONO_NETREG_STATUS_NOT_REGISTERED;
|
||||
} else {
|
||||
/*
|
||||
* TODO: need a way to make sure that SPDI information has
|
||||
* already been read from the SIM (i.e. sim_spdi_read_cb in
|
||||
* network.c has been called)
|
||||
*/
|
||||
return binder_netreg_check_if_really_roaming(self->watch->netreg,
|
||||
status);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_gprs_data_update_registration_state(
|
||||
BinderGprs* self)
|
||||
{
|
||||
const enum ofono_netreg_status status = binder_gprs_fix_registration_status
|
||||
(self, self->network->data.status);
|
||||
|
||||
if (self->reg_status != status) {
|
||||
ofono_info("data reg changed %d -> %d (%s), attached %d",
|
||||
self->reg_status, status, ofono_netreg_status_to_string(status),
|
||||
self->attached);
|
||||
self->reg_status = status;
|
||||
ofono_gprs_status_notify(self->gprs, self->reg_status);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_gprs_check_data_allowed(
|
||||
BinderGprs* self)
|
||||
{
|
||||
DBG_(self, "%d %d", binder_data_allowed(self->data), self->attached);
|
||||
if (!binder_data_allowed(self->data) && self->attached) {
|
||||
self->attached = FALSE;
|
||||
if (self->gprs) {
|
||||
ofono_gprs_detached_notify(self->gprs);
|
||||
}
|
||||
}
|
||||
|
||||
binder_gprs_data_update_registration_state(self);
|
||||
}
|
||||
|
||||
static
|
||||
gboolean
|
||||
binder_gprs_set_attached_cb(
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderGprsCbData* cbd = user_data;
|
||||
BinderGprs* self = cbd->self;
|
||||
struct ofono_error err;
|
||||
|
||||
GASSERT(self->set_attached_id);
|
||||
self->set_attached_id = 0;
|
||||
binder_gprs_check_data_allowed(self);
|
||||
cbd->cb(binder_error_ok(&err), cbd->data);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_gprs_set_attached(
|
||||
struct ofono_gprs* gprs,
|
||||
int attached,
|
||||
ofono_gprs_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
BinderGprs* self = binder_gprs_get_data(gprs);
|
||||
struct ofono_error err;
|
||||
|
||||
if (binder_data_allowed(self->data) || !attached) {
|
||||
DBG_(self, "attached: %d", attached);
|
||||
if (self->set_attached_id) {
|
||||
g_source_remove(self->set_attached_id);
|
||||
}
|
||||
self->attached = attached;
|
||||
self->set_attached_id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,
|
||||
binder_gprs_set_attached_cb, binder_gprs_cbd_new(self, cb, data),
|
||||
binder_gprs_cbd_free);
|
||||
} else {
|
||||
DBG_(self, "not allowed to attach");
|
||||
cb(binder_error_failure(&err), data);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_gprs_allow_data_changed(
|
||||
BinderData* data,
|
||||
BINDER_DATA_PROPERTY property,
|
||||
void* user_data)
|
||||
{
|
||||
BinderGprs* self = user_data;
|
||||
|
||||
DBG_(self, "%d", binder_data_allowed(data));
|
||||
if (!self->set_attached_id) {
|
||||
binder_gprs_check_data_allowed(self);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_gprs_max_data_calls_changed(
|
||||
BinderNetwork* net,
|
||||
BINDER_NETWORK_PROPERTY property,
|
||||
void* user_data)
|
||||
{
|
||||
BinderGprs* self = user_data;
|
||||
|
||||
if (net->max_data_calls > 0) {
|
||||
DBG_(self, "setting max cids to %d", net->max_data_calls);
|
||||
ofono_gprs_set_cid_range(self->gprs, 1, net->max_data_calls);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_gprs_data_registration_state_changed(
|
||||
BinderNetwork* net,
|
||||
BINDER_NETWORK_PROPERTY property,
|
||||
void* user_data)
|
||||
{
|
||||
binder_gprs_data_update_registration_state((BinderGprs*)user_data);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_gprs_registration_status(
|
||||
struct ofono_gprs* gprs,
|
||||
ofono_gprs_status_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
BinderGprs* self = binder_gprs_get_data(gprs);
|
||||
struct ofono_error err;
|
||||
const enum ofono_netreg_status status = self->attached ?
|
||||
self->reg_status : OFONO_NETREG_STATUS_NOT_REGISTERED;
|
||||
|
||||
DBG("%d (%s)", status, ofono_netreg_status_to_string(status));
|
||||
cb(binder_error_ok(&err), status, data);
|
||||
}
|
||||
|
||||
static
|
||||
gboolean
|
||||
binder_gprs_register(
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderGprs* self = user_data;
|
||||
BinderNetwork* network = self->network;
|
||||
struct ofono_gprs* gprs = self->gprs;
|
||||
|
||||
self->init_id = 0;
|
||||
self->network_event_id[NETWORK_EVENT_DATA_STATE] =
|
||||
binder_network_add_property_handler(network,
|
||||
BINDER_NETWORK_PROPERTY_DATA_STATE,
|
||||
binder_gprs_data_registration_state_changed, self);
|
||||
self->network_event_id[NETWORK_EVENT_MAX_DATA_CALLS] =
|
||||
binder_network_add_property_handler(network,
|
||||
BINDER_NETWORK_PROPERTY_MAX_DATA_CALLS,
|
||||
binder_gprs_max_data_calls_changed, self);
|
||||
self->data_event_id =
|
||||
binder_data_add_property_handler(self->data,
|
||||
BINDER_DATA_PROPERTY_ALLOWED,
|
||||
binder_gprs_allow_data_changed, self);
|
||||
self->reg_status = binder_gprs_fix_registration_status(self,
|
||||
network->data.status);
|
||||
|
||||
if (network->max_data_calls > 0) {
|
||||
DBG_(self, "setting max cids to %d", network->max_data_calls);
|
||||
ofono_gprs_set_cid_range(gprs, 1,network->max_data_calls);
|
||||
}
|
||||
|
||||
ofono_gprs_register(gprs);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
binder_gprs_probe(
|
||||
struct ofono_gprs* gprs,
|
||||
unsigned int vendor,
|
||||
void* data)
|
||||
{
|
||||
BinderModem* modem = binder_modem_get_data(data);
|
||||
BinderGprs* self = g_new0(BinderGprs, 1);
|
||||
|
||||
self->log_prefix = binder_dup_prefix(modem->log_prefix);
|
||||
DBG_(self, "");
|
||||
|
||||
self->watch = ofono_watch_new(binder_modem_get_path(modem));
|
||||
self->data = binder_data_ref(modem->data);
|
||||
self->network = binder_network_ref(modem->network);
|
||||
self->gprs = gprs;
|
||||
|
||||
ofono_gprs_set_data(gprs, self);
|
||||
self->init_id = g_idle_add(binder_gprs_register, self);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_gprs_remove(
|
||||
struct ofono_gprs* gprs)
|
||||
{
|
||||
BinderGprs* self = binder_gprs_get_data(gprs);
|
||||
|
||||
DBG_(self, "");
|
||||
|
||||
if (self->set_attached_id) {
|
||||
g_source_remove(self->set_attached_id);
|
||||
}
|
||||
|
||||
if (self->init_id) {
|
||||
g_source_remove(self->init_id);
|
||||
}
|
||||
|
||||
binder_network_remove_all_handlers(self->network, self->network_event_id);
|
||||
binder_network_unref(self->network);
|
||||
|
||||
binder_data_remove_handler(self->data, self->data_event_id);
|
||||
binder_data_unref(self->data);
|
||||
|
||||
ofono_watch_unref(self->watch);
|
||||
g_free(self->log_prefix);
|
||||
g_free(self);
|
||||
|
||||
ofono_gprs_set_data(gprs, NULL);
|
||||
}
|
||||
|
||||
/*==========================================================================*
|
||||
* API
|
||||
*==========================================================================*/
|
||||
|
||||
static const struct ofono_gprs_driver binder_gprs_driver = {
|
||||
.name = BINDER_DRIVER,
|
||||
.probe = binder_gprs_probe,
|
||||
.remove = binder_gprs_remove,
|
||||
.set_attached = binder_gprs_set_attached,
|
||||
.attached_status = binder_gprs_registration_status
|
||||
};
|
||||
|
||||
void
|
||||
binder_gprs_init()
|
||||
{
|
||||
ofono_gprs_driver_register(&binder_gprs_driver);
|
||||
}
|
||||
|
||||
void
|
||||
binder_gprs_cleanup()
|
||||
{
|
||||
ofono_gprs_driver_unregister(&binder_gprs_driver);
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
37
src/binder_gprs.h
Normal file
37
src/binder_gprs.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef BINDER_GPRS_H
|
||||
#define BINDER_GPRS_H
|
||||
|
||||
#include "binder_types.h"
|
||||
|
||||
void
|
||||
binder_gprs_init(void)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_gprs_cleanup(void)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
#endif /* BINDER_GPRS_H */
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
735
src/binder_gprs_context.c
Normal file
735
src/binder_gprs_context.c
Normal file
@@ -0,0 +1,735 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "binder_data.h"
|
||||
#include "binder_gprs_context.h"
|
||||
#include "binder_log.h"
|
||||
#include "binder_modem.h"
|
||||
#include "binder_network.h"
|
||||
#include "binder_netreg.h"
|
||||
#include "binder_util.h"
|
||||
|
||||
#include <ofono/log.h>
|
||||
#include <ofono/mtu-limit.h>
|
||||
#include <ofono/watch.h>
|
||||
|
||||
#include <gutil_strv.h>
|
||||
#include <gutil_misc.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#define CTX_ID_NONE ((unsigned int)(-1))
|
||||
|
||||
#define MAX_MMS_MTU 1280
|
||||
|
||||
typedef struct binder_gprs_context_call {
|
||||
BinderDataRequest* req;
|
||||
ofono_gprs_context_cb_t cb;
|
||||
gpointer data;
|
||||
unsigned int cid;
|
||||
} BinderGprsContextCall;
|
||||
|
||||
typedef struct binder_gprs_context {
|
||||
struct ofono_gprs_context* gc;
|
||||
struct ofono_watch* watch;
|
||||
struct ofono_mtu_limit* mtu_limit;
|
||||
BinderNetwork* network;
|
||||
BinderData* data;
|
||||
char* log_prefix;
|
||||
guint active_ctx_cid;
|
||||
gulong calls_changed_id;
|
||||
BinderDataCall* active_call;
|
||||
BinderGprsContextCall activate;
|
||||
BinderGprsContextCall deactivate;
|
||||
} BinderGprsContext;
|
||||
|
||||
#define DBG_(self,fmt,args...) DBG("%s" fmt, (self)->log_prefix, ##args)
|
||||
|
||||
static inline BinderGprsContext*
|
||||
binder_gprs_context_get_data(struct ofono_gprs_context *gprs)
|
||||
{ return ofono_gprs_context_get_data(gprs); }
|
||||
|
||||
static
|
||||
char*
|
||||
binder_gprs_context_netmask(
|
||||
const char* bits)
|
||||
{
|
||||
guint nbits;
|
||||
|
||||
if (gutil_parse_uint(bits, 0, &nbits) && nbits < 33) {
|
||||
const char* str;
|
||||
struct in_addr in;
|
||||
|
||||
in.s_addr = htonl((nbits == 32) ? 0xffffffff :
|
||||
((1u << nbits)-1) << (32-nbits));
|
||||
str = inet_ntoa(in);
|
||||
if (str) {
|
||||
return g_strdup(str);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
binder_gprs_context_address_family(
|
||||
const char* addr)
|
||||
{
|
||||
if (strchr(addr, ':')) {
|
||||
return AF_INET6;
|
||||
} else if (strchr(addr, '.')) {
|
||||
return AF_INET;
|
||||
} else {
|
||||
return AF_UNSPEC;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_gprs_context_free_active_call(
|
||||
BinderGprsContext* self)
|
||||
{
|
||||
if (self->active_call) {
|
||||
binder_data_call_release(self->data, self->active_call->cid, self);
|
||||
binder_data_call_free(self->active_call);
|
||||
self->active_call = NULL;
|
||||
}
|
||||
if (self->calls_changed_id) {
|
||||
binder_data_remove_handler(self->data, self->calls_changed_id);
|
||||
self->calls_changed_id = 0;
|
||||
}
|
||||
if (self->mtu_limit) {
|
||||
ofono_mtu_limit_free(self->mtu_limit);
|
||||
self->mtu_limit = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_gprs_context_set_active_call(
|
||||
BinderGprsContext* self,
|
||||
const BinderDataCall* call)
|
||||
{
|
||||
if (call) {
|
||||
binder_data_call_free(self->active_call);
|
||||
self->active_call = binder_data_call_dup(call);
|
||||
if (ofono_gprs_context_get_type(self->gc) ==
|
||||
OFONO_GPRS_CONTEXT_TYPE_MMS) {
|
||||
/*
|
||||
* Some MMS providers have a problem with MTU greater than 1280.
|
||||
* Let's be safe.
|
||||
*/
|
||||
if (!self->mtu_limit) {
|
||||
self->mtu_limit = ofono_mtu_limit_new(MAX_MMS_MTU);
|
||||
}
|
||||
}
|
||||
ofono_mtu_limit_set_ifname(self->mtu_limit, call->ifname);
|
||||
binder_data_call_grab(self->data, call->cid, self);
|
||||
} else {
|
||||
binder_gprs_context_free_active_call(self);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_gprs_context_set_disconnected(
|
||||
BinderGprsContext* self)
|
||||
{
|
||||
if (self->active_call) {
|
||||
binder_gprs_context_free_active_call(self);
|
||||
if (self->deactivate.req) {
|
||||
BinderGprsContextCall deact = self->deactivate;
|
||||
|
||||
memset(&self->deactivate, 0, sizeof(self->deactivate));
|
||||
binder_data_request_cancel(deact.req);
|
||||
if (deact.cb) {
|
||||
struct ofono_error err;
|
||||
|
||||
ofono_info("Deactivated data call");
|
||||
deact.cb(binder_error_ok(&err), deact.data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (self->active_ctx_cid != CTX_ID_NONE) {
|
||||
const guint id = self->active_ctx_cid;
|
||||
|
||||
self->active_ctx_cid = CTX_ID_NONE;
|
||||
DBG_(self, "ofono context %u deactivated", id);
|
||||
ofono_gprs_context_deactivated(self->gc, id);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_gprs_context_set_address(
|
||||
struct ofono_gprs_context *gc,
|
||||
const BinderDataCall* call)
|
||||
{
|
||||
const char* ip_addr = NULL;
|
||||
char* ip_mask = NULL;
|
||||
const char* ipv6_addr = NULL;
|
||||
unsigned char ipv6_prefix_length = 0;
|
||||
char* tmp_ip_addr = NULL;
|
||||
char* tmp_ipv6_addr = NULL;
|
||||
char* const* list = call->addresses;
|
||||
const int n = gutil_strv_length(list);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < n && (!ipv6_addr || !ip_addr); i++) {
|
||||
const char* addr = list[i];
|
||||
|
||||
switch (binder_gprs_context_address_family(addr)) {
|
||||
case AF_INET:
|
||||
if (!ip_addr) {
|
||||
const char* s = strchr(addr, '/');
|
||||
|
||||
if (s) {
|
||||
const gsize len = s - addr;
|
||||
tmp_ip_addr = g_strndup(addr, len);
|
||||
ip_addr = tmp_ip_addr;
|
||||
ip_mask = binder_gprs_context_netmask(s+1);
|
||||
} else {
|
||||
ip_addr = addr;
|
||||
}
|
||||
if (!ip_mask) {
|
||||
ip_mask = g_strdup("255.255.255.0");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AF_INET6:
|
||||
if (!ipv6_addr) {
|
||||
const char* s = strchr(addr, '/');
|
||||
|
||||
if (s) {
|
||||
const gsize len = s - addr;
|
||||
const int prefix = atoi(s + 1);
|
||||
|
||||
tmp_ipv6_addr = g_strndup(addr, len);
|
||||
ipv6_addr = tmp_ipv6_addr;
|
||||
if (prefix >= 0 && prefix <= 128) {
|
||||
ipv6_prefix_length = prefix;
|
||||
}
|
||||
} else {
|
||||
ipv6_addr = addr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ofono_gprs_context_set_ipv4_address(gc, ip_addr, TRUE);
|
||||
ofono_gprs_context_set_ipv4_netmask(gc, ip_mask);
|
||||
ofono_gprs_context_set_ipv6_address(gc, ipv6_addr);
|
||||
ofono_gprs_context_set_ipv6_prefix_length(gc, ipv6_prefix_length);
|
||||
|
||||
if (!ip_addr && !ipv6_addr) {
|
||||
ofono_error("GPRS context: No IP address");
|
||||
}
|
||||
|
||||
/* Allocate temporary strings */
|
||||
g_free(ip_mask);
|
||||
g_free(tmp_ip_addr);
|
||||
g_free(tmp_ipv6_addr);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_gprs_context_set_gateway(
|
||||
struct ofono_gprs_context *gc,
|
||||
const BinderDataCall* call)
|
||||
{
|
||||
const char* ip_gw = NULL;
|
||||
const char* ipv6_gw = NULL;
|
||||
char* const* list = call->gateways;
|
||||
const int n = gutil_strv_length(list);
|
||||
int i;
|
||||
|
||||
/* Pick 1 gw for each protocol*/
|
||||
for (i = 0; i < n && (!ipv6_gw || !ip_gw); i++) {
|
||||
const char* addr = list[i];
|
||||
switch (binder_gprs_context_address_family(addr)) {
|
||||
case AF_INET:
|
||||
if (!ip_gw) ip_gw = addr;
|
||||
break;
|
||||
case AF_INET6:
|
||||
if (!ipv6_gw) ipv6_gw = addr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ofono_gprs_context_set_ipv4_gateway(gc, ip_gw);
|
||||
ofono_gprs_context_set_ipv6_gateway(gc, ipv6_gw);
|
||||
}
|
||||
|
||||
typedef
|
||||
void
|
||||
(*ofono_gprs_context_list_setter_t)(
|
||||
struct ofono_gprs_context* gc,
|
||||
const char** list);
|
||||
|
||||
static
|
||||
void
|
||||
binder_gprs_context_set_servers(
|
||||
struct ofono_gprs_context* gc,
|
||||
char* const* list,
|
||||
ofono_gprs_context_list_setter_t set_ipv4,
|
||||
ofono_gprs_context_list_setter_t set_ipv6)
|
||||
{
|
||||
int i;
|
||||
const char** ip_list = NULL, ** ip_ptr = NULL;
|
||||
const char** ipv6_list = NULL, ** ipv6_ptr = NULL;
|
||||
const int n = gutil_strv_length(list);
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
const char *addr = list[i];
|
||||
|
||||
switch (binder_gprs_context_address_family(addr)) {
|
||||
case AF_INET:
|
||||
if (!ip_ptr) {
|
||||
ip_list = g_new0(const char *, n - i + 1);
|
||||
ip_ptr = ip_list;
|
||||
}
|
||||
*ip_ptr++ = addr;
|
||||
break;
|
||||
case AF_INET6:
|
||||
if (!ipv6_ptr) {
|
||||
ipv6_list = g_new0(const char *, n - i + 1);
|
||||
ipv6_ptr = ipv6_list;
|
||||
}
|
||||
*ipv6_ptr++ = addr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
set_ipv4(gc, ip_list);
|
||||
set_ipv6(gc, ipv6_list);
|
||||
|
||||
g_free(ip_list);
|
||||
g_free(ipv6_list);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_gprs_context_set_dns_servers(
|
||||
struct ofono_gprs_context* gc,
|
||||
const BinderDataCall* call)
|
||||
{
|
||||
binder_gprs_context_set_servers(gc, call->dnses,
|
||||
ofono_gprs_context_set_ipv4_dns_servers,
|
||||
ofono_gprs_context_set_ipv6_dns_servers);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_gprs_context_set_proxy_cscf(
|
||||
struct ofono_gprs_context* gc,
|
||||
const BinderDataCall* call)
|
||||
{
|
||||
binder_gprs_context_set_servers(gc, call->pcscf,
|
||||
ofono_gprs_context_set_ipv4_proxy_cscf,
|
||||
ofono_gprs_context_set_ipv6_proxy_cscf);
|
||||
}
|
||||
|
||||
/* Only compares the stuff that's important to us */
|
||||
|
||||
#define DATA_CALL_IFNAME_CHANGED (0x01)
|
||||
#define DATA_CALL_ADDRESS_CHANGED (0x02)
|
||||
#define DATA_CALL_GATEWAY_CHANGED (0x04)
|
||||
#define DATA_CALL_DNS_CHANGED (0x08)
|
||||
#define DATA_CALL_PCSCF_CHANGED (0x10)
|
||||
#define DATA_CALL_ALL_CHANGED (0x1f)
|
||||
|
||||
static
|
||||
int
|
||||
binder_gprs_context_data_call_change(
|
||||
const BinderDataCall* c1,
|
||||
const BinderDataCall* c2)
|
||||
{
|
||||
if (c1 == c2) {
|
||||
return 0;
|
||||
} else if (c1 && c2) {
|
||||
int changes = 0;
|
||||
|
||||
if (g_strcmp0(c1->ifname, c2->ifname)) {
|
||||
changes |= DATA_CALL_IFNAME_CHANGED;
|
||||
}
|
||||
|
||||
if (!gutil_strv_equal(c1->addresses, c2->addresses)) {
|
||||
changes |= DATA_CALL_ADDRESS_CHANGED;
|
||||
}
|
||||
|
||||
if (!gutil_strv_equal(c1->gateways, c2->gateways)) {
|
||||
changes |= DATA_CALL_GATEWAY_CHANGED;
|
||||
}
|
||||
|
||||
if (!gutil_strv_equal(c1->dnses, c2->dnses)) {
|
||||
changes |= DATA_CALL_DNS_CHANGED;
|
||||
}
|
||||
|
||||
if (!gutil_strv_equal(c1->pcscf, c2->pcscf)) {
|
||||
changes |= DATA_CALL_PCSCF_CHANGED;
|
||||
}
|
||||
|
||||
return changes;
|
||||
} else {
|
||||
return DATA_CALL_ALL_CHANGED;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_gprs_context_call_list_changed(
|
||||
BinderData* data,
|
||||
BINDER_DATA_PROPERTY property,
|
||||
void* arg)
|
||||
{
|
||||
BinderGprsContext* self = arg;
|
||||
struct ofono_gprs_context* gc = self->gc;
|
||||
|
||||
/*
|
||||
* self->active_call can't be NULL here because this callback
|
||||
* is only registered when we have the active call and released
|
||||
* when active call is dropped.
|
||||
*/
|
||||
BinderDataCall* prev_call = self->active_call;
|
||||
const BinderDataCall* call = binder_data_call_find(data->calls,
|
||||
prev_call->cid);
|
||||
int change = 0;
|
||||
|
||||
if (call && call->active != RADIO_DATA_CALL_INACTIVE) {
|
||||
/* Compare it against the last known state */
|
||||
change = binder_gprs_context_data_call_change(call, prev_call);
|
||||
} else {
|
||||
ofono_error("Clearing active context");
|
||||
binder_gprs_context_set_disconnected(self);
|
||||
call = NULL;
|
||||
}
|
||||
|
||||
if (!call) {
|
||||
/* We are not interested */
|
||||
return;
|
||||
} else if (!change) {
|
||||
DBG_(self, "call %u didn't change", call->cid);
|
||||
return;
|
||||
} else {
|
||||
DBG_(self, "call %u changed", call->cid);
|
||||
}
|
||||
|
||||
/*
|
||||
* prev_call points to the previous active call, and it will
|
||||
* be deallocated at the end of the this function. Clear the
|
||||
* self->active_call pointer so that we don't deallocate it twice.
|
||||
*/
|
||||
self->active_call = NULL;
|
||||
binder_gprs_context_set_active_call(self, call);
|
||||
|
||||
if (call->status != RADIO_DATA_CALL_FAIL_NONE) {
|
||||
ofono_info("data call status: %d", call->status);
|
||||
}
|
||||
|
||||
if (change & DATA_CALL_IFNAME_CHANGED) {
|
||||
DBG_(self, "interface changed");
|
||||
ofono_gprs_context_set_interface(gc, call->ifname);
|
||||
}
|
||||
|
||||
if (change & DATA_CALL_ADDRESS_CHANGED) {
|
||||
DBG_(self, "address changed");
|
||||
binder_gprs_context_set_address(gc, call);
|
||||
}
|
||||
|
||||
if (change & DATA_CALL_GATEWAY_CHANGED) {
|
||||
DBG_(self, "gateway changed");
|
||||
binder_gprs_context_set_gateway(gc, call);
|
||||
}
|
||||
|
||||
if (change & DATA_CALL_DNS_CHANGED) {
|
||||
DBG_(self, "name server(s) changed");
|
||||
binder_gprs_context_set_dns_servers(gc, call);
|
||||
}
|
||||
|
||||
if (change & DATA_CALL_PCSCF_CHANGED) {
|
||||
DBG_(self, "P-CSCF changed");
|
||||
binder_gprs_context_set_proxy_cscf(gc, call);
|
||||
}
|
||||
|
||||
ofono_gprs_context_signal_change(gc, self->active_ctx_cid);
|
||||
binder_data_call_free(prev_call);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_gprs_context_activate_primary_cb(
|
||||
BinderData* data,
|
||||
RADIO_ERROR radio_error,
|
||||
const BinderDataCall* call,
|
||||
void* user_data)
|
||||
{
|
||||
BinderGprsContext* self = user_data;
|
||||
struct ofono_gprs_context* gc = self->gc;
|
||||
struct ofono_error error;
|
||||
ofono_gprs_context_cb_t cb;
|
||||
gpointer cb_data;
|
||||
|
||||
binder_error_init_failure(&error);
|
||||
if (radio_error != RADIO_ERROR_NONE) {
|
||||
ofono_error("GPRS context: Reply failure: %s",
|
||||
binder_radio_error_string(radio_error));
|
||||
} else if (!call) {
|
||||
ofono_error("Unexpected data call failure");
|
||||
} else if (call->status != RADIO_DATA_CALL_FAIL_NONE) {
|
||||
ofono_error("Unexpected data call status %d", call->status);
|
||||
error.type = OFONO_ERROR_TYPE_CMS;
|
||||
error.error = call->status;
|
||||
} else if (!call->ifname) {
|
||||
/* Must have interface */
|
||||
ofono_error("GPRS context: No interface");
|
||||
} else {
|
||||
ofono_info("setting up data call");
|
||||
|
||||
GASSERT(!self->calls_changed_id);
|
||||
binder_data_remove_handler(self->data, self->calls_changed_id);
|
||||
self->calls_changed_id = binder_data_add_property_handler(self->data,
|
||||
BINDER_DATA_PROPERTY_CALLS, binder_gprs_context_call_list_changed,
|
||||
self);
|
||||
|
||||
self->active_ctx_cid = self->activate.cid;
|
||||
binder_gprs_context_set_active_call(self, call);
|
||||
ofono_gprs_context_set_interface(gc, call->ifname);
|
||||
binder_gprs_context_set_address(gc, call);
|
||||
binder_gprs_context_set_gateway(gc, call);
|
||||
binder_gprs_context_set_dns_servers(gc, call);
|
||||
binder_gprs_context_set_proxy_cscf(gc, call);
|
||||
binder_error_init_ok(&error);
|
||||
}
|
||||
|
||||
cb = self->activate.cb;
|
||||
cb_data = self->activate.data;
|
||||
GASSERT(self->activate.req);
|
||||
memset(&self->activate, 0, sizeof(self->activate));
|
||||
|
||||
if (cb) {
|
||||
cb(&error, cb_data);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_gprs_context_activate_primary(
|
||||
struct ofono_gprs_context* gc,
|
||||
const struct ofono_gprs_primary_context* ctx,
|
||||
ofono_gprs_context_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
BinderGprsContext* self = binder_gprs_context_get_data(gc);
|
||||
struct ofono_watch* watch = self->watch;
|
||||
struct ofono_netreg* netreg = watch->netreg;
|
||||
const enum ofono_netreg_status rs = ofono_netreg_get_status(netreg);
|
||||
|
||||
/* Let's make sure that we aren't connecting when roaming not allowed */
|
||||
if (rs == OFONO_NETREG_STATUS_ROAMING) {
|
||||
struct ofono_gprs* gprs = watch->gprs;
|
||||
|
||||
if (!ofono_gprs_get_roaming_allowed(gprs) &&
|
||||
binder_netreg_check_if_really_roaming(netreg, rs) ==
|
||||
OFONO_NETREG_STATUS_ROAMING) {
|
||||
struct ofono_error error;
|
||||
|
||||
ofono_info("Can't activate context %u (roaming)", ctx->cid);
|
||||
cb(binder_error_failure(&error), data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ofono_info("Activating context: %u", ctx->cid);
|
||||
GASSERT(!self->activate.req);
|
||||
GASSERT(ctx->cid != CTX_ID_NONE);
|
||||
|
||||
self->activate.cb = cb;
|
||||
self->activate.data = data;
|
||||
self->activate.cid = ctx->cid;
|
||||
self->activate.req = binder_data_call_setup(self->data, ctx,
|
||||
ofono_gprs_context_get_assigned_type(gc),
|
||||
binder_gprs_context_activate_primary_cb, self);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_gprs_context_deactivate_primary_cb(
|
||||
BinderData* data,
|
||||
RADIO_ERROR radio_error,
|
||||
void* user_data)
|
||||
{
|
||||
BinderGprsContext* gcd = user_data;
|
||||
|
||||
/*
|
||||
* Data call list may change before the completion of the deactivate
|
||||
* request, in that case binder_gprs_context_set_disconnected will be
|
||||
* invoked and gcd->deactivate.req will be NULL.
|
||||
*/
|
||||
if (gcd->deactivate.req) {
|
||||
ofono_gprs_context_cb_t cb = gcd->deactivate.cb;
|
||||
gpointer cb_data = gcd->deactivate.data;
|
||||
|
||||
if (radio_error == RADIO_ERROR_NONE) {
|
||||
GASSERT(gcd->active_call);
|
||||
ofono_info("Deactivated data call");
|
||||
} else {
|
||||
ofono_error("Deactivate failure: %s",
|
||||
binder_radio_error_string(radio_error));
|
||||
}
|
||||
|
||||
memset(&gcd->deactivate, 0, sizeof(gcd->deactivate));
|
||||
if (cb) {
|
||||
struct ofono_error error;
|
||||
|
||||
binder_gprs_context_free_active_call(gcd);
|
||||
cb(binder_error_ok(&error), cb_data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Make sure we are in the disconnected state */
|
||||
binder_gprs_context_set_disconnected(gcd);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_gprs_context_deactivate_primary(
|
||||
struct ofono_gprs_context* gc,
|
||||
unsigned int id,
|
||||
ofono_gprs_context_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
BinderGprsContext* self = binder_gprs_context_get_data(gc);
|
||||
|
||||
GASSERT(self->active_ctx_cid == id);
|
||||
ofono_info("Deactivating context: %u", id);
|
||||
|
||||
if (self->active_call && self->active_ctx_cid == id) {
|
||||
self->deactivate.cb = cb;
|
||||
self->deactivate.data = data;
|
||||
self->deactivate.req = binder_data_call_deactivate(self->data,
|
||||
self->active_call->cid, binder_gprs_context_deactivate_primary_cb,
|
||||
self);
|
||||
} else if (cb) {
|
||||
struct ofono_error err;
|
||||
|
||||
cb(binder_error_ok(&err), data);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_gprs_context_detach_shutdown(
|
||||
struct ofono_gprs_context* gc,
|
||||
unsigned int id)
|
||||
{
|
||||
binder_gprs_context_deactivate_primary(gc, id, NULL, NULL);
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
binder_gprs_context_probe(
|
||||
struct ofono_gprs_context* gc,
|
||||
unsigned int vendor,
|
||||
void* data)
|
||||
{
|
||||
BinderModem* modem = binder_modem_get_data(data);
|
||||
BinderGprsContext* self = g_new0(BinderGprsContext, 1);
|
||||
|
||||
self->log_prefix = binder_dup_prefix(modem->log_prefix);
|
||||
DBG_(self, "");
|
||||
|
||||
self->gc = gc;
|
||||
self->watch = ofono_watch_new(binder_modem_get_path(modem));
|
||||
self->network = binder_network_ref(modem->network);
|
||||
self->data = binder_data_ref(modem->data);
|
||||
self->active_ctx_cid = CTX_ID_NONE;
|
||||
|
||||
ofono_gprs_context_set_data(gc, self);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_gprs_context_remove(
|
||||
struct ofono_gprs_context* gc)
|
||||
{
|
||||
BinderGprsContext* self = binder_gprs_context_get_data(gc);
|
||||
|
||||
DBG_(self, "");
|
||||
if (self->activate.req) {
|
||||
/*
|
||||
* The core has already completed its pending D-Bus
|
||||
* request, invoking the completion callback will
|
||||
* cause libdbus to panic.
|
||||
*/
|
||||
binder_data_request_detach(self->activate.req);
|
||||
binder_data_request_cancel(self->activate.req);
|
||||
}
|
||||
|
||||
if (self->deactivate.req) {
|
||||
/* Let it complete but we won't be around to be notified. */
|
||||
binder_data_request_detach(self->deactivate.req);
|
||||
} else if (self->active_call) {
|
||||
binder_data_call_deactivate(self->data, self->active_call->cid,
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
binder_data_remove_handler(self->data, self->calls_changed_id);
|
||||
binder_data_unref(self->data);
|
||||
binder_network_unref(self->network);
|
||||
binder_data_call_free(self->active_call);
|
||||
ofono_mtu_limit_free(self->mtu_limit);
|
||||
ofono_watch_unref(self->watch);
|
||||
|
||||
g_free(self->log_prefix);
|
||||
g_free(self);
|
||||
|
||||
ofono_gprs_context_set_data(gc, NULL);
|
||||
}
|
||||
|
||||
/*==========================================================================*
|
||||
* API
|
||||
*==========================================================================*/
|
||||
|
||||
static const struct ofono_gprs_context_driver binder_gprs_context_driver = {
|
||||
.name = BINDER_DRIVER,
|
||||
.probe = binder_gprs_context_probe,
|
||||
.remove = binder_gprs_context_remove,
|
||||
.activate_primary = binder_gprs_context_activate_primary,
|
||||
.deactivate_primary = binder_gprs_context_deactivate_primary,
|
||||
.detach_shutdown = binder_gprs_context_detach_shutdown
|
||||
};
|
||||
|
||||
void
|
||||
binder_gprs_context_init()
|
||||
{
|
||||
ofono_gprs_context_driver_register(&binder_gprs_context_driver);
|
||||
}
|
||||
|
||||
void
|
||||
binder_gprs_context_cleanup()
|
||||
{
|
||||
ofono_gprs_context_driver_unregister(&binder_gprs_context_driver);
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
37
src/binder_gprs_context.h
Normal file
37
src/binder_gprs_context.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef BINDER_GPRS_CONTEXT_H
|
||||
#define BINDER_GPRS_CONTEXT_H
|
||||
|
||||
#include "binder_types.h"
|
||||
|
||||
void
|
||||
binder_gprs_context_init(void)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_gprs_context_cleanup(void)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
#endif /* BINDER_GPRS_CONTEXT_H */
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
31
src/binder_log.h
Normal file
31
src/binder_log.h
Normal file
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef BINDER_LOG_H
|
||||
#define BINDER_LOG_H
|
||||
|
||||
#define GLOG_MODULE_NAME binder_plugin_log
|
||||
#include <gutil_log.h>
|
||||
#include <ofono/log.h>
|
||||
|
||||
#endif /* BINDER_LOG_H */
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
553
src/binder_logger.c
Normal file
553
src/binder_logger.c
Normal file
@@ -0,0 +1,553 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "binder_logger.h"
|
||||
#include "binder_util.h"
|
||||
|
||||
#include <radio_config.h>
|
||||
#include <radio_instance.h>
|
||||
#include <radio_util.h>
|
||||
|
||||
#include <gbinder_local_request.h>
|
||||
#include <gbinder_reader.h>
|
||||
#include <gbinder_writer.h>
|
||||
|
||||
#include <gutil_log.h>
|
||||
|
||||
enum binder_logger_events {
|
||||
EVENT_REQ,
|
||||
EVENT_RESP,
|
||||
EVENT_IND,
|
||||
EVENT_ACK,
|
||||
EVENT_COUNT
|
||||
};
|
||||
|
||||
typedef struct binder_logger_callbacks {
|
||||
const char* (*req_name)(guint32 code);
|
||||
const char* (*resp_name)(guint32 code);
|
||||
const char* (*ind_name)(guint32 code);
|
||||
gsize (*rpc_header_size)(gpointer object, guint32 code);
|
||||
void (*drop_object)(BinderLogger* logger);
|
||||
} BinderLoggerCallbacks;
|
||||
|
||||
struct binder_logger {
|
||||
const BinderLoggerCallbacks* cb;
|
||||
gpointer object;
|
||||
gulong event_id[EVENT_COUNT];
|
||||
char* prefix;
|
||||
};
|
||||
|
||||
#define CONFIG_PREFIX "config"
|
||||
|
||||
GLogModule binder_logger_module = {
|
||||
.max_level = GLOG_LEVEL_VERBOSE,
|
||||
.level = GLOG_LEVEL_VERBOSE,
|
||||
.flags = GLOG_FLAG_HIDE_NAME
|
||||
};
|
||||
|
||||
static GLogModule binder_logger_dump_module = {
|
||||
.parent = &binder_logger_module,
|
||||
.max_level = GLOG_LEVEL_VERBOSE,
|
||||
.level = GLOG_LEVEL_INHERIT,
|
||||
.flags = GLOG_FLAG_HIDE_NAME
|
||||
};
|
||||
|
||||
static
|
||||
void
|
||||
binder_logger_trace_req(
|
||||
BinderLogger* logger,
|
||||
guint code,
|
||||
GBinderLocalRequest* args)
|
||||
{
|
||||
const BinderLoggerCallbacks* cb = logger->cb;
|
||||
static const GLogModule* log = &binder_logger_module;
|
||||
const gsize header_size = cb->rpc_header_size(logger->object, code);
|
||||
const char* name = cb->req_name(code);
|
||||
GBinderWriter writer;
|
||||
const guint8* data;
|
||||
guint32 serial;
|
||||
gsize size;
|
||||
|
||||
/* Use writer API to fetch the raw data and extract the serial */
|
||||
gbinder_local_request_init_writer(args, &writer);
|
||||
data = gbinder_writer_get_data(&writer, &size);
|
||||
serial = (size >= header_size + 4) ? *(guint32*)(data + header_size) : 0;
|
||||
|
||||
if (serial) {
|
||||
gutil_log(log, GLOG_LEVEL_VERBOSE, "%s< [%08x] %u %s",
|
||||
logger->prefix, serial, code, name ? name : "");
|
||||
} else {
|
||||
gutil_log(log, GLOG_LEVEL_VERBOSE, "%s< %u %s",
|
||||
logger->prefix, code, name ? name : "");
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_logger_trace_resp(
|
||||
BinderLogger* logger,
|
||||
guint code,
|
||||
const RadioResponseInfo* info,
|
||||
const GBinderReader* args)
|
||||
{
|
||||
static const GLogModule* log = &binder_logger_module;
|
||||
const BinderLoggerCallbacks* cb = logger->cb;
|
||||
const char* name = cb->resp_name(code);
|
||||
const char* error = (info->error == RADIO_ERROR_NONE) ? NULL :
|
||||
binder_radio_error_string(info->error);
|
||||
const char* arg1 = name ? name : error;
|
||||
const char* arg2 = name ? error : NULL;
|
||||
|
||||
if (arg2) {
|
||||
gutil_log(log, GLOG_LEVEL_VERBOSE, "%s> [%08x] %u %s %s",
|
||||
logger->prefix, info->serial, code, arg1, arg2);
|
||||
} else if (arg1) {
|
||||
gutil_log(log, GLOG_LEVEL_VERBOSE, "%s> [%08x] %u %s",
|
||||
logger->prefix, info->serial, code, arg1);
|
||||
} else {
|
||||
gutil_log(log, GLOG_LEVEL_VERBOSE, "%s> [%08x] %u",
|
||||
logger->prefix, info->serial, code);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_logger_trace_ind(
|
||||
BinderLogger* logger,
|
||||
RADIO_IND code,
|
||||
const GBinderReader* args)
|
||||
{
|
||||
const BinderLoggerCallbacks* cb = logger->cb;
|
||||
const char* name = cb->ind_name(code);
|
||||
static const GLogModule* log = &binder_logger_module;
|
||||
|
||||
gutil_log(log, GLOG_LEVEL_VERBOSE, "%s> %u %s",
|
||||
logger->prefix, code, name ? name : "");
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_logger_dump_req(
|
||||
GBinderLocalRequest* args)
|
||||
{
|
||||
GBinderWriter writer;
|
||||
const guint8* data;
|
||||
gsize size;
|
||||
|
||||
/* Use writer API to fetch the raw data */
|
||||
gbinder_local_request_init_writer(args, &writer);
|
||||
data = gbinder_writer_get_data(&writer, &size);
|
||||
gutil_log_dump(&binder_logger_dump_module, GLOG_LEVEL_VERBOSE, " ",
|
||||
data, size);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_logger_dump_reader(
|
||||
const GBinderReader* reader)
|
||||
{
|
||||
gsize size;
|
||||
const guint8* data = gbinder_reader_get_data(reader, &size);
|
||||
|
||||
gutil_log_dump(&binder_logger_dump_module, GLOG_LEVEL_VERBOSE, " ",
|
||||
data, size);
|
||||
}
|
||||
|
||||
/*==========================================================================*
|
||||
* RadioInstance implementation
|
||||
*==========================================================================*/
|
||||
|
||||
static
|
||||
void
|
||||
binder_logger_radio_trace_req_cb(
|
||||
RadioInstance* radio,
|
||||
RADIO_REQ code,
|
||||
GBinderLocalRequest* args,
|
||||
gpointer user_data)
|
||||
{
|
||||
binder_logger_trace_req((BinderLogger*)user_data, code, args);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_logger_radio_trace_resp_cb(
|
||||
RadioInstance* radio,
|
||||
RADIO_RESP code,
|
||||
const RadioResponseInfo* info,
|
||||
const GBinderReader* args,
|
||||
gpointer user_data)
|
||||
{
|
||||
binder_logger_trace_resp((BinderLogger*)user_data, code, info, args);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_logger_radio_trace_ind_cb(
|
||||
RadioInstance* radio,
|
||||
RADIO_IND code,
|
||||
RADIO_IND_TYPE type,
|
||||
const GBinderReader* args,
|
||||
gpointer user_data)
|
||||
{
|
||||
binder_logger_trace_ind((BinderLogger*)user_data, code, args);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_logger_radio_trace_ack_cb(
|
||||
RadioInstance* radio,
|
||||
guint32 serial,
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderLogger* logger = user_data;
|
||||
|
||||
gutil_log(&binder_logger_module, GLOG_LEVEL_VERBOSE, "%s> [%08x] "
|
||||
"acknowledgeRequest", logger->prefix, serial);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_logger_radio_dump_req_cb(
|
||||
RadioInstance* radio,
|
||||
RADIO_REQ code,
|
||||
GBinderLocalRequest* args,
|
||||
gpointer user_data)
|
||||
{
|
||||
binder_logger_dump_req(args);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_logger_radio_dump_resp_cb(
|
||||
RadioInstance* radio,
|
||||
RADIO_RESP code,
|
||||
const RadioResponseInfo* info,
|
||||
const GBinderReader* args,
|
||||
gpointer user_data)
|
||||
{
|
||||
binder_logger_dump_reader(args);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_logger_radio_dump_ind_cb(
|
||||
RadioInstance* radio,
|
||||
RADIO_IND code,
|
||||
RADIO_IND_TYPE type,
|
||||
const GBinderReader* args,
|
||||
gpointer user_data)
|
||||
{
|
||||
binder_logger_dump_reader(args);
|
||||
}
|
||||
|
||||
static
|
||||
const char*
|
||||
binder_logger_radio_req_name(
|
||||
guint32 code)
|
||||
{
|
||||
return radio_req_name(code);
|
||||
}
|
||||
|
||||
static
|
||||
const char*
|
||||
binder_logger_radio_resp_name(
|
||||
guint32 code)
|
||||
{
|
||||
return radio_resp_name(code);
|
||||
}
|
||||
|
||||
static
|
||||
const char*
|
||||
binder_logger_radio_ind_name(
|
||||
guint32 code)
|
||||
{
|
||||
return radio_ind_name(code);
|
||||
}
|
||||
|
||||
static
|
||||
gsize
|
||||
binder_logger_radio_rpc_header_size(
|
||||
gpointer object,
|
||||
guint32 code)
|
||||
{
|
||||
return radio_instance_rpc_header_size(object, code);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_logger_radio_drop_object(
|
||||
BinderLogger* logger)
|
||||
{
|
||||
radio_instance_remove_all_handlers(logger->object, logger->event_id);
|
||||
radio_instance_unref(logger->object);
|
||||
}
|
||||
|
||||
static
|
||||
BinderLogger*
|
||||
binder_logger_radio_new(
|
||||
RadioInstance* radio,
|
||||
const char* prefix,
|
||||
RADIO_INSTANCE_PRIORITY pri,
|
||||
RadioRequestObserverFunc req_cb,
|
||||
RadioResponseObserverFunc resp_cb,
|
||||
RadioIndicationObserverFunc ind_cb,
|
||||
RadioAckFunc ack_cb)
|
||||
{
|
||||
static BinderLoggerCallbacks binder_logger_radio_callbacks = {
|
||||
.req_name = binder_logger_radio_req_name,
|
||||
.resp_name = binder_logger_radio_resp_name,
|
||||
.ind_name = binder_logger_radio_ind_name,
|
||||
.rpc_header_size = binder_logger_radio_rpc_header_size,
|
||||
.drop_object = binder_logger_radio_drop_object
|
||||
};
|
||||
|
||||
if (radio) {
|
||||
BinderLogger* logger = g_new0(BinderLogger, 1);
|
||||
|
||||
logger->cb = &binder_logger_radio_callbacks;
|
||||
logger->prefix = binder_dup_prefix(prefix);
|
||||
logger->object = radio_instance_ref(radio);
|
||||
logger->event_id[EVENT_REQ] =
|
||||
radio_instance_add_request_observer_with_priority(radio, pri,
|
||||
RADIO_REQ_ANY, req_cb, logger);
|
||||
logger->event_id[EVENT_RESP] =
|
||||
radio_instance_add_response_observer_with_priority(radio, pri,
|
||||
RADIO_RESP_ANY, resp_cb, logger);
|
||||
logger->event_id[EVENT_IND] =
|
||||
radio_instance_add_indication_observer_with_priority(radio, pri,
|
||||
RADIO_IND_ANY, ind_cb, logger);
|
||||
logger->event_id[EVENT_ACK] = radio_instance_add_ack_handler(radio,
|
||||
ack_cb, logger);
|
||||
return logger;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*==========================================================================*
|
||||
* RadioConfig implementation
|
||||
*==========================================================================*/
|
||||
|
||||
static
|
||||
void
|
||||
binder_logger_config_trace_req_cb(
|
||||
RadioConfig* config,
|
||||
RADIO_CONFIG_REQ code,
|
||||
GBinderLocalRequest* args,
|
||||
gpointer user_data)
|
||||
{
|
||||
binder_logger_trace_req((BinderLogger*)user_data, code, args);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_logger_config_trace_resp_cb(
|
||||
RadioConfig* config,
|
||||
RADIO_CONFIG_RESP code,
|
||||
const RadioResponseInfo* info,
|
||||
const GBinderReader* args,
|
||||
gpointer user_data)
|
||||
{
|
||||
binder_logger_trace_resp((BinderLogger*)user_data, code, info, args);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_logger_config_trace_ind_cb(
|
||||
RadioConfig* config,
|
||||
RADIO_CONFIG_IND code,
|
||||
const GBinderReader* args,
|
||||
gpointer user_data)
|
||||
{
|
||||
binder_logger_trace_ind((BinderLogger*)user_data, code, args);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_logger_config_dump_req_cb(
|
||||
RadioConfig* config,
|
||||
RADIO_CONFIG_REQ code,
|
||||
GBinderLocalRequest* args,
|
||||
gpointer user_data)
|
||||
{
|
||||
binder_logger_dump_req(args);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_logger_config_dump_resp_cb(
|
||||
RadioConfig* config,
|
||||
RADIO_CONFIG_RESP code,
|
||||
const RadioResponseInfo* info,
|
||||
const GBinderReader* args,
|
||||
gpointer user_data)
|
||||
{
|
||||
binder_logger_dump_reader(args);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_logger_config_dump_ind_cb(
|
||||
RadioConfig* config,
|
||||
RADIO_CONFIG_IND code,
|
||||
const GBinderReader* args,
|
||||
gpointer user_data)
|
||||
{
|
||||
binder_logger_dump_reader(args);
|
||||
}
|
||||
|
||||
static
|
||||
const char*
|
||||
binder_logger_config_req_name(
|
||||
guint32 code)
|
||||
{
|
||||
return radio_config_req_name(NULL, code);
|
||||
}
|
||||
|
||||
static
|
||||
const char*
|
||||
binder_logger_config_resp_name(
|
||||
guint32 code)
|
||||
{
|
||||
return radio_config_resp_name(NULL, code);
|
||||
}
|
||||
|
||||
static
|
||||
const char*
|
||||
binder_logger_config_ind_name(
|
||||
guint32 code)
|
||||
{
|
||||
return radio_config_ind_name(NULL, code);
|
||||
}
|
||||
|
||||
static
|
||||
gsize
|
||||
binder_logger_config_rpc_header_size(
|
||||
gpointer object,
|
||||
guint32 code)
|
||||
{
|
||||
return radio_config_rpc_header_size(object, code);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_logger_config_drop_object(
|
||||
BinderLogger* logger)
|
||||
{
|
||||
radio_config_remove_all_handlers(logger->object, logger->event_id);
|
||||
radio_config_unref(logger->object);
|
||||
}
|
||||
|
||||
static
|
||||
BinderLogger*
|
||||
binder_logger_config_new(
|
||||
RadioConfig* config,
|
||||
const char* prefix,
|
||||
RADIO_INSTANCE_PRIORITY pri,
|
||||
RadioConfigRequestObserverFunc req_cb,
|
||||
RadioConfigResponseObserverFunc resp_cb,
|
||||
RadioConfigIndicationObserverFunc ind_cb)
|
||||
{
|
||||
static BinderLoggerCallbacks binder_logger_config_callbacks = {
|
||||
.req_name = binder_logger_config_req_name,
|
||||
.resp_name = binder_logger_config_resp_name,
|
||||
.ind_name = binder_logger_config_ind_name,
|
||||
.rpc_header_size = binder_logger_config_rpc_header_size,
|
||||
.drop_object = binder_logger_config_drop_object
|
||||
};
|
||||
|
||||
if (config) {
|
||||
BinderLogger* logger = g_new0(BinderLogger, 1);
|
||||
|
||||
logger->cb = &binder_logger_config_callbacks;
|
||||
logger->prefix = binder_dup_prefix(prefix);
|
||||
logger->object = radio_config_ref(config);
|
||||
logger->event_id[EVENT_REQ] =
|
||||
radio_config_add_request_observer_with_priority(config, pri,
|
||||
RADIO_REQ_ANY, req_cb, logger);
|
||||
logger->event_id[EVENT_RESP] =
|
||||
radio_config_add_response_observer_with_priority(config, pri,
|
||||
RADIO_RESP_ANY, resp_cb, logger);
|
||||
logger->event_id[EVENT_IND] =
|
||||
radio_config_add_indication_observer_with_priority(config, pri,
|
||||
RADIO_IND_ANY, ind_cb, logger);
|
||||
return logger;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/*==========================================================================*
|
||||
* API
|
||||
*==========================================================================*/
|
||||
|
||||
BinderLogger*
|
||||
binder_logger_new_radio_trace(
|
||||
RadioInstance* radio,
|
||||
const char* prefix)
|
||||
{
|
||||
return binder_logger_radio_new(radio, prefix,
|
||||
RADIO_INSTANCE_PRIORITY_HIGHEST, binder_logger_radio_trace_req_cb,
|
||||
binder_logger_radio_trace_resp_cb, binder_logger_radio_trace_ind_cb,
|
||||
binder_logger_radio_trace_ack_cb);
|
||||
}
|
||||
|
||||
BinderLogger*
|
||||
binder_logger_new_radio_dump(
|
||||
RadioInstance* radio,
|
||||
const char* prefix)
|
||||
{
|
||||
return binder_logger_radio_new(radio, prefix,
|
||||
RADIO_INSTANCE_PRIORITY_HIGHEST - 1, binder_logger_radio_dump_req_cb,
|
||||
binder_logger_radio_dump_resp_cb, binder_logger_radio_dump_ind_cb,
|
||||
NULL);
|
||||
}
|
||||
|
||||
BinderLogger*
|
||||
binder_logger_new_config_trace(
|
||||
RadioConfig* config)
|
||||
{
|
||||
return binder_logger_config_new(config, CONFIG_PREFIX,
|
||||
RADIO_INSTANCE_PRIORITY_HIGHEST, binder_logger_config_trace_req_cb,
|
||||
binder_logger_config_trace_resp_cb, binder_logger_config_trace_ind_cb);
|
||||
}
|
||||
|
||||
BinderLogger*
|
||||
binder_logger_new_config_dump(
|
||||
RadioConfig* config)
|
||||
{
|
||||
return binder_logger_config_new(config, CONFIG_PREFIX,
|
||||
RADIO_INSTANCE_PRIORITY_HIGHEST - 1, binder_logger_config_dump_req_cb,
|
||||
binder_logger_config_dump_resp_cb, binder_logger_config_dump_ind_cb);
|
||||
}
|
||||
|
||||
void
|
||||
binder_logger_free(
|
||||
BinderLogger* logger)
|
||||
{
|
||||
if (logger) {
|
||||
logger->cb->drop_object(logger);
|
||||
g_free(logger->prefix);
|
||||
g_free(logger);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
58
src/binder_logger.h
Normal file
58
src/binder_logger.h
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef BINDER_LOGGER_H
|
||||
#define BINDER_LOGGER_H
|
||||
|
||||
#include "binder_types.h"
|
||||
|
||||
BinderLogger*
|
||||
binder_logger_new_radio_trace(
|
||||
RadioInstance* instance,
|
||||
const char* prefix)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
BinderLogger*
|
||||
binder_logger_new_radio_dump(
|
||||
RadioInstance* instance,
|
||||
const char* prefix)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
BinderLogger*
|
||||
binder_logger_new_config_trace(
|
||||
RadioConfig* config)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
BinderLogger*
|
||||
binder_logger_new_config_dump(
|
||||
RadioConfig* config)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_logger_free(
|
||||
BinderLogger* logger)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
extern GLogModule binder_logger_module BINDER_INTERNAL;
|
||||
|
||||
#endif /* BINDER_LOGGER_H */
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
646
src/binder_modem.c
Normal file
646
src/binder_modem.c
Normal file
@@ -0,0 +1,646 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "binder_modem.h"
|
||||
#include "binder_network.h"
|
||||
#include "binder_radio.h"
|
||||
#include "binder_sim_card.h"
|
||||
#include "binder_sim_settings.h"
|
||||
#include "binder_cell_info.h"
|
||||
#include "binder_data.h"
|
||||
#include "binder_util.h"
|
||||
#include "binder_log.h"
|
||||
|
||||
#include <ofono/call-barring.h>
|
||||
#include <ofono/call-forwarding.h>
|
||||
#include <ofono/call-settings.h>
|
||||
#include <ofono/call-volume.h>
|
||||
#include <ofono/cbs.h>
|
||||
#include <ofono/cell-info.h>
|
||||
#include <ofono/devinfo.h>
|
||||
#include <ofono/gprs.h>
|
||||
#include <ofono/message-waiting.h>
|
||||
#include <ofono/modem.h>
|
||||
#include <ofono/netmon.h>
|
||||
#include <ofono/phonebook.h>
|
||||
#include <ofono/sim-auth.h>
|
||||
#include <ofono/sim.h>
|
||||
#include <ofono/sms.h>
|
||||
#include <ofono/stk.h>
|
||||
#include <ofono/ussd.h>
|
||||
#include <ofono/voicecall.h>
|
||||
#include <ofono/watch.h>
|
||||
|
||||
#include <radio_client.h>
|
||||
#include <radio_request.h>
|
||||
#include <radio_request_group.h>
|
||||
|
||||
#include <gutil_macros.h>
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#define ONLINE_TIMEOUT_SECS (15) /* 20 sec is hardcoded in ofono core */
|
||||
|
||||
typedef enum binder_modem_power_state {
|
||||
POWERED_OFF,
|
||||
POWERED_ON,
|
||||
POWERING_OFF
|
||||
} BINDER_MODEM_POWER_STATE;
|
||||
|
||||
enum binder_modem_watch_event {
|
||||
WATCH_IMSI,
|
||||
WATCH_ICCID,
|
||||
WATCH_SIM_STATE,
|
||||
WATCH_EVENT_COUNT
|
||||
};
|
||||
|
||||
typedef struct binder_modem_priv BinderModemPriv;
|
||||
|
||||
typedef struct binder_modem_online_request {
|
||||
const char* name;
|
||||
BinderModemPriv* self;
|
||||
ofono_modem_online_cb_t cb;
|
||||
void* data;
|
||||
guint timeout_id;
|
||||
} BinderModemOnlineRequest;
|
||||
|
||||
struct binder_modem_priv {
|
||||
BinderModem pub;
|
||||
RadioRequestGroup* g;
|
||||
char* log_prefix;
|
||||
char* imeisv;
|
||||
char* imei;
|
||||
|
||||
gulong watch_event_id[WATCH_EVENT_COUNT];
|
||||
char* last_known_iccid;
|
||||
char* reset_iccid;
|
||||
|
||||
guint online_check_id;
|
||||
BINDER_MODEM_POWER_STATE power_state;
|
||||
gulong radio_state_event_id;
|
||||
|
||||
BinderModemOnlineRequest set_online;
|
||||
BinderModemOnlineRequest set_offline;
|
||||
};
|
||||
|
||||
#define RADIO_POWER_TAG(md) (md)
|
||||
|
||||
#define DBG_(self,fmt,args...) DBG("%s" fmt, (self)->log_prefix, ##args)
|
||||
|
||||
static BinderModemPriv* binder_modem_cast(BinderModem* modem)
|
||||
{ return G_CAST(modem, BinderModemPriv, pub); }
|
||||
static BinderModemPriv* binder_modem_get_priv(struct ofono_modem* ofono)
|
||||
{ return binder_modem_cast(binder_modem_get_data(ofono)); }
|
||||
|
||||
static
|
||||
void
|
||||
binder_modem_online_request_done(
|
||||
BinderModemOnlineRequest* req)
|
||||
{
|
||||
if (req->cb) {
|
||||
struct ofono_error error;
|
||||
ofono_modem_online_cb_t cb = req->cb;
|
||||
void* data = req->data;
|
||||
|
||||
req->cb = NULL;
|
||||
req->data = NULL;
|
||||
DBG_(req->self, "%s", req->name);
|
||||
cb(binder_error_ok(&error), data);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_modem_online_request_ok(
|
||||
BinderModemOnlineRequest* req)
|
||||
{
|
||||
if (req->timeout_id) {
|
||||
g_source_remove(req->timeout_id);
|
||||
req->timeout_id = 0;
|
||||
}
|
||||
binder_modem_online_request_done(req);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_modem_update_online_state(
|
||||
BinderModemPriv* self)
|
||||
{
|
||||
switch (self->pub.radio->state) {
|
||||
case RADIO_STATE_ON:
|
||||
DBG_(self, "online");
|
||||
binder_modem_online_request_ok(&self->set_online);
|
||||
break;
|
||||
|
||||
case RADIO_STATE_OFF:
|
||||
case RADIO_STATE_UNAVAILABLE:
|
||||
DBG_(self, "offline");
|
||||
binder_modem_online_request_ok(&self->set_offline);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!self->set_offline.timeout_id &&
|
||||
!self->set_online.timeout_id &&
|
||||
self->power_state == POWERING_OFF) {
|
||||
self->power_state = POWERED_OFF;
|
||||
struct ofono_modem* ofono = self->pub.ofono;
|
||||
|
||||
if (ofono) {
|
||||
ofono_modem_set_powered(ofono, FALSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
gboolean
|
||||
binder_modem_online_request_timeout(
|
||||
gpointer data)
|
||||
{
|
||||
BinderModemOnlineRequest* req = data;
|
||||
|
||||
GASSERT(req->timeout_id);
|
||||
req->timeout_id = 0;
|
||||
DBG_(req->self, "%s", req->name);
|
||||
binder_modem_online_request_done(req);
|
||||
binder_modem_update_online_state(req->self);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static
|
||||
gboolean
|
||||
binder_modem_online_check(
|
||||
gpointer data)
|
||||
{
|
||||
BinderModemPriv* self = data;
|
||||
|
||||
GASSERT(self->online_check_id);
|
||||
self->online_check_id = 0;
|
||||
binder_modem_update_online_state(self);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_modem_schedule_online_check(
|
||||
BinderModemPriv* self)
|
||||
{
|
||||
if (!self->online_check_id) {
|
||||
self->online_check_id = g_idle_add(binder_modem_online_check, self);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_modem_update_radio_settings(
|
||||
BinderModemPriv* self)
|
||||
{
|
||||
BinderModem* modem = &self->pub;
|
||||
struct ofono_watch* watch = modem->watch;
|
||||
struct ofono_radio_settings* rs =
|
||||
ofono_modem_get_radio_settings(modem->ofono);
|
||||
|
||||
if (watch->imsi) {
|
||||
/* radio-settings.c assumes that IMSI is available */
|
||||
if ((modem->config.features & BINDER_FEATURE_RADIO_SETTINGS) && !rs) {
|
||||
DBG_(self, "initializing radio settings interface");
|
||||
ofono_radio_settings_create(modem->ofono, 0, BINDER_DRIVER,
|
||||
modem->ofono);
|
||||
}
|
||||
} else if (rs) {
|
||||
DBG_(self, "removing radio settings interface");
|
||||
ofono_radio_settings_remove(rs);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_modem_radio_state_cb(
|
||||
BinderRadio* radio,
|
||||
BINDER_RADIO_PROPERTY property,
|
||||
void* data)
|
||||
{
|
||||
binder_modem_update_online_state((BinderModemPriv*)data);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_modem_imsi_cb(
|
||||
struct ofono_watch* watch,
|
||||
void* data)
|
||||
{
|
||||
binder_modem_update_radio_settings((BinderModemPriv*)data);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_modem_iccid_cb(
|
||||
struct ofono_watch* watch,
|
||||
void* data)
|
||||
{
|
||||
BinderModemPriv* self = data;
|
||||
|
||||
if (watch->iccid) {
|
||||
g_free(self->last_known_iccid);
|
||||
self->last_known_iccid = g_strdup(watch->iccid);
|
||||
DBG_(self, "%s", self->last_known_iccid);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_modem_sim_state_cb(
|
||||
struct ofono_watch* watch,
|
||||
void* data)
|
||||
{
|
||||
BinderModemPriv* self = data;
|
||||
const enum ofono_sim_state state = ofono_sim_get_state(watch->sim);
|
||||
|
||||
if (state == OFONO_SIM_STATE_RESETTING) {
|
||||
g_free(self->reset_iccid);
|
||||
self->reset_iccid = self->last_known_iccid;
|
||||
self->last_known_iccid = NULL;
|
||||
DBG_(self, "%s is resetting", self->reset_iccid);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_modem_pre_sim(
|
||||
struct ofono_modem* ofono)
|
||||
{
|
||||
BinderModem* modem = binder_modem_get_data(ofono);
|
||||
BinderModemPriv* self = binder_modem_cast(modem);
|
||||
const BINDER_FEATURE_MASK features = modem->config.features;
|
||||
|
||||
DBG_(self, "");
|
||||
ofono_devinfo_create(ofono, 0, BINDER_DRIVER, ofono);
|
||||
ofono_sim_create(ofono, 0, BINDER_DRIVER, ofono);
|
||||
if (features & BINDER_FEATURE_VOICE) {
|
||||
ofono_voicecall_create(ofono, 0, BINDER_DRIVER, ofono);
|
||||
}
|
||||
if (!self->radio_state_event_id) {
|
||||
self->radio_state_event_id =
|
||||
binder_radio_add_property_handler(modem->radio,
|
||||
BINDER_RADIO_PROPERTY_STATE,
|
||||
binder_modem_radio_state_cb, self);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_modem_post_sim(
|
||||
struct ofono_modem* ofono)
|
||||
{
|
||||
BinderModem* modem = binder_modem_get_data(ofono);
|
||||
BinderModemPriv* self = binder_modem_cast(modem);
|
||||
const BINDER_FEATURE_MASK features = modem->config.features;
|
||||
struct ofono_gprs* gprs;
|
||||
|
||||
DBG_(self, "");
|
||||
ofono_call_forwarding_create(ofono, 0, BINDER_DRIVER, ofono);
|
||||
ofono_call_barring_create(ofono, 0, BINDER_DRIVER, ofono);
|
||||
ofono_message_waiting_register(ofono_message_waiting_create(ofono));
|
||||
if (features & BINDER_FEATURE_SMS) {
|
||||
ofono_sms_create(ofono, 0, BINDER_DRIVER, ofono);
|
||||
}
|
||||
if (features & BINDER_FEATURE_DATA) {
|
||||
gprs = ofono_gprs_create(ofono, 0, BINDER_DRIVER, ofono);
|
||||
if (gprs) {
|
||||
guint i;
|
||||
static const enum ofono_gprs_context_type ap_types[] = {
|
||||
OFONO_GPRS_CONTEXT_TYPE_INTERNET,
|
||||
OFONO_GPRS_CONTEXT_TYPE_MMS,
|
||||
OFONO_GPRS_CONTEXT_TYPE_IMS
|
||||
};
|
||||
|
||||
/* Create a context for each type */
|
||||
for (i = 0; i < G_N_ELEMENTS(ap_types); i++) {
|
||||
struct ofono_gprs_context* gc =
|
||||
ofono_gprs_context_create(ofono, 0, BINDER_DRIVER, ofono);
|
||||
|
||||
if (gc == NULL) {
|
||||
break;
|
||||
} else {
|
||||
ofono_gprs_context_set_type(gc, ap_types[i]);
|
||||
ofono_gprs_add_context(gprs, gc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (features & BINDER_FEATURE_PHONEBOOK) {
|
||||
ofono_phonebook_create(ofono, 0, "generic", ofono);
|
||||
}
|
||||
if (features & BINDER_FEATURE_STK) {
|
||||
struct ofono_watch* watch = modem->watch;
|
||||
|
||||
if (!self->reset_iccid || g_strcmp0(self->reset_iccid, watch->iccid)) {
|
||||
/* This SIM was never reset */
|
||||
ofono_stk_create(ofono, 0, BINDER_DRIVER, ofono);
|
||||
} else {
|
||||
ofono_warn("Disabling STK after SIM reset");
|
||||
}
|
||||
}
|
||||
if (features & BINDER_FEATURE_CBS) {
|
||||
ofono_cbs_create(ofono, 0, BINDER_DRIVER, ofono);
|
||||
}
|
||||
if (features & BINDER_FEATURE_SIM_AUTH) {
|
||||
ofono_sim_auth_create(ofono);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_modem_post_online(
|
||||
struct ofono_modem* ofono)
|
||||
{
|
||||
BinderModem* modem = binder_modem_get_data(ofono);
|
||||
const BINDER_FEATURE_MASK features = modem->config.features;
|
||||
|
||||
DBG_(binder_modem_cast(modem),"");
|
||||
ofono_call_volume_create(ofono, 0, BINDER_DRIVER, ofono);
|
||||
ofono_call_settings_create(ofono, 0, BINDER_DRIVER, ofono);
|
||||
if (features & BINDER_FEATURE_NETREG) {
|
||||
ofono_netreg_create(ofono, 0, BINDER_DRIVER, ofono);
|
||||
}
|
||||
if (features & BINDER_FEATURE_USSD) {
|
||||
ofono_ussd_create(ofono, 0, BINDER_DRIVER, ofono);
|
||||
}
|
||||
ofono_netmon_create(ofono, 0, "cellinfo", ofono);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_modem_set_online(
|
||||
struct ofono_modem* ofono,
|
||||
ofono_bool_t online,
|
||||
ofono_modem_online_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
BinderModem* modem = binder_modem_get_data(ofono);
|
||||
BinderModemPriv* self = binder_modem_cast(modem);
|
||||
struct binder_radio* radio = modem->radio;
|
||||
BinderModemOnlineRequest* req;
|
||||
|
||||
DBG_(self, "going %sline", online ? "on" : "off");
|
||||
|
||||
binder_radio_set_online(radio, online);
|
||||
if (online) {
|
||||
binder_radio_power_on(radio, RADIO_POWER_TAG(self));
|
||||
req = &self->set_online;
|
||||
} else {
|
||||
binder_radio_power_off(radio, RADIO_POWER_TAG(self));
|
||||
req = &self->set_offline;
|
||||
}
|
||||
|
||||
req->cb = cb;
|
||||
req->data = data;
|
||||
if (req->timeout_id) {
|
||||
g_source_remove(req->timeout_id);
|
||||
}
|
||||
req->timeout_id = g_timeout_add_seconds(ONLINE_TIMEOUT_SECS,
|
||||
binder_modem_online_request_timeout, req);
|
||||
binder_modem_schedule_online_check(self);
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
binder_modem_enable(
|
||||
struct ofono_modem* ofono)
|
||||
{
|
||||
BinderModemPriv* self = binder_modem_get_priv(ofono);
|
||||
|
||||
DBG_(self, "");
|
||||
self->power_state = POWERED_ON;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
binder_modem_disable(
|
||||
struct ofono_modem* ofono)
|
||||
{
|
||||
BinderModemPriv* self = binder_modem_get_priv(ofono);
|
||||
|
||||
DBG_(self, "");
|
||||
if (self->set_online.timeout_id || self->set_offline.timeout_id) {
|
||||
self->power_state = POWERING_OFF;
|
||||
return -EINPROGRESS;
|
||||
} else {
|
||||
self->power_state = POWERED_OFF;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
binder_modem_probe(
|
||||
struct ofono_modem* ofono)
|
||||
{
|
||||
DBG("%s", ofono_modem_get_path(ofono));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_modem_remove(
|
||||
struct ofono_modem* ofono)
|
||||
{
|
||||
BinderModem* modem = binder_modem_get_data(ofono);
|
||||
BinderModemPriv* self = binder_modem_cast(modem);
|
||||
|
||||
DBG_(self, "");
|
||||
ofono_modem_set_data(ofono, NULL);
|
||||
|
||||
binder_radio_remove_handler(modem->radio, self->radio_state_event_id);
|
||||
binder_radio_set_online(modem->radio, FALSE);
|
||||
binder_radio_power_off(modem->radio, RADIO_POWER_TAG(self));
|
||||
binder_radio_set_online(modem->radio, FALSE);
|
||||
binder_radio_unref(modem->radio);
|
||||
binder_sim_settings_unref(modem->sim_settings);
|
||||
|
||||
ofono_watch_remove_all_handlers(modem->watch, self->watch_event_id);
|
||||
ofono_watch_unref(modem->watch);
|
||||
|
||||
if (self->online_check_id) {
|
||||
g_source_remove(self->online_check_id);
|
||||
}
|
||||
if (self->set_online.timeout_id) {
|
||||
g_source_remove(self->set_online.timeout_id);
|
||||
}
|
||||
if (self->set_offline.timeout_id) {
|
||||
g_source_remove(self->set_offline.timeout_id);
|
||||
}
|
||||
|
||||
binder_network_unref(modem->network);
|
||||
binder_sim_card_unref(modem->sim_card);
|
||||
binder_data_unref(modem->data);
|
||||
ofono_cell_info_unref(modem->cell_info);
|
||||
|
||||
radio_request_group_cancel(self->g);
|
||||
radio_request_group_unref(self->g);
|
||||
radio_client_unref(modem->client);
|
||||
|
||||
g_free(self->last_known_iccid);
|
||||
g_free(self->reset_iccid);
|
||||
g_free(self->log_prefix);
|
||||
g_free(self->imeisv);
|
||||
g_free(self->imei);
|
||||
g_free(self);
|
||||
}
|
||||
|
||||
/*==========================================================================*
|
||||
* API
|
||||
*==========================================================================*/
|
||||
|
||||
static const struct ofono_modem_driver binder_modem_driver = {
|
||||
.name = BINDER_DRIVER,
|
||||
.probe = binder_modem_probe,
|
||||
.remove = binder_modem_remove,
|
||||
.enable = binder_modem_enable,
|
||||
.disable = binder_modem_disable,
|
||||
.pre_sim = binder_modem_pre_sim,
|
||||
.post_sim = binder_modem_post_sim,
|
||||
.post_online = binder_modem_post_online,
|
||||
.set_online = binder_modem_set_online
|
||||
};
|
||||
|
||||
void
|
||||
binder_modem_init()
|
||||
{
|
||||
ofono_modem_driver_register(&binder_modem_driver);
|
||||
}
|
||||
|
||||
void
|
||||
binder_modem_cleanup()
|
||||
{
|
||||
ofono_modem_driver_unregister(&binder_modem_driver);
|
||||
}
|
||||
|
||||
BinderModem*
|
||||
binder_modem_create(
|
||||
RadioClient* client,
|
||||
const char* log_prefix,
|
||||
const char* path,
|
||||
const char* imei,
|
||||
const char* imeisv,
|
||||
const BinderSlotConfig* config,
|
||||
BinderRadio* radio,
|
||||
BinderNetwork* network,
|
||||
BinderSimCard* card,
|
||||
BinderData* data,
|
||||
BinderSimSettings* settings,
|
||||
struct ofono_cell_info* cell_info)
|
||||
{
|
||||
/* Skip the slash from the path, it looks like "/ril_0" */
|
||||
struct ofono_modem* ofono = ofono_modem_create(path + 1, BINDER_DRIVER);
|
||||
|
||||
if (ofono) {
|
||||
int err;
|
||||
BinderModemPriv* self = g_new0(BinderModemPriv, 1);
|
||||
BinderModem* modem = &self->pub;
|
||||
|
||||
/*
|
||||
* binder_plugin.c must wait until IMEI becomes known before
|
||||
* creating the modem
|
||||
*/
|
||||
GASSERT(imei);
|
||||
|
||||
/* Copy config */
|
||||
modem->config = *config;
|
||||
modem->imei = self->imei = g_strdup(imei);
|
||||
modem->imeisv = self->imeisv = g_strdup(imeisv);
|
||||
modem->log_prefix = self->log_prefix = binder_dup_prefix(log_prefix);
|
||||
modem->ofono = ofono;
|
||||
modem->radio = binder_radio_ref(radio);
|
||||
modem->network = binder_network_ref(network);
|
||||
modem->sim_card = binder_sim_card_ref(card);
|
||||
modem->sim_settings = binder_sim_settings_ref(settings);
|
||||
modem->cell_info = ofono_cell_info_ref(cell_info);
|
||||
modem->data = binder_data_ref(data);
|
||||
modem->watch = ofono_watch_new(path);
|
||||
modem->client = radio_client_ref(client);
|
||||
self->g = radio_request_group_new(client);
|
||||
self->last_known_iccid = g_strdup(modem->watch->iccid);
|
||||
|
||||
self->watch_event_id[WATCH_IMSI] =
|
||||
ofono_watch_add_imsi_changed_handler(modem->watch,
|
||||
binder_modem_imsi_cb, self);
|
||||
self->watch_event_id[WATCH_ICCID] =
|
||||
ofono_watch_add_iccid_changed_handler(modem->watch,
|
||||
binder_modem_iccid_cb, self);
|
||||
self->watch_event_id[WATCH_SIM_STATE] =
|
||||
ofono_watch_add_sim_state_changed_handler(modem->watch,
|
||||
binder_modem_sim_state_cb, self);
|
||||
|
||||
self->set_online.name = "online";
|
||||
self->set_online.self = self;
|
||||
self->set_offline.name = "offline";
|
||||
self->set_offline.self = self;
|
||||
ofono_modem_set_data(ofono, self);
|
||||
err = ofono_modem_register(ofono);
|
||||
if (!err) {
|
||||
if (config->radio_power_cycle) {
|
||||
binder_radio_power_cycle(modem->radio);
|
||||
}
|
||||
|
||||
/*
|
||||
* ofono_modem_reset() sets Powered to TRUE without
|
||||
* issuing PropertyChange signal.
|
||||
*/
|
||||
ofono_modem_set_powered(modem->ofono, FALSE);
|
||||
ofono_modem_set_powered(modem->ofono, TRUE);
|
||||
self->power_state = POWERED_ON;
|
||||
|
||||
/*
|
||||
* With some implementations, querying available band modes
|
||||
* causes some magic Android properties to appear. That's
|
||||
* the only reason for making this call.
|
||||
*/
|
||||
if (config->query_available_band_mode) {
|
||||
/* oneway getAvailableBandModes(int32 serial); */
|
||||
RadioRequest* req = radio_request_new2(self->g,
|
||||
RADIO_REQ_GET_AVAILABLE_BAND_MODES, NULL,
|
||||
NULL, NULL, NULL);
|
||||
|
||||
radio_request_submit(req);
|
||||
radio_request_unref(req);
|
||||
}
|
||||
|
||||
binder_modem_update_radio_settings(self);
|
||||
return modem;
|
||||
} else {
|
||||
ofono_error("Error %d registering %s", err, BINDER_DRIVER);
|
||||
|
||||
/*
|
||||
* If ofono_modem_register() failed, then
|
||||
* ofono_modem_remove() won't invoke
|
||||
* binder_modem_remove() callback.
|
||||
*/
|
||||
binder_modem_remove(ofono);
|
||||
}
|
||||
|
||||
ofono_modem_remove(ofono);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
75
src/binder_modem.h
Normal file
75
src/binder_modem.h
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef BINDER_MODEM_H
|
||||
#define BINDER_MODEM_H
|
||||
|
||||
#include "binder_types.h"
|
||||
|
||||
#include <ofono/modem.h>
|
||||
|
||||
struct binder_modem {
|
||||
RadioClient* client;
|
||||
const char* path;
|
||||
const char* log_prefix;
|
||||
const char* imei;
|
||||
const char* imeisv;
|
||||
struct ofono_modem* ofono;
|
||||
struct ofono_cell_info* cell_info;
|
||||
struct ofono_watch* watch;
|
||||
BinderData* data;
|
||||
BinderNetwork* network;
|
||||
BinderRadio* radio;
|
||||
BinderSimCard* sim_card;
|
||||
BinderSimSettings* sim_settings;
|
||||
BinderSlotConfig config;
|
||||
};
|
||||
|
||||
#define binder_modem_get_path(modem) ofono_modem_get_path((modem)->ofono)
|
||||
#define binder_modem_get_data(modem) ((BinderModem*)ofono_modem_get_data(modem))
|
||||
|
||||
void
|
||||
binder_modem_init(void)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_modem_cleanup(void)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
BinderModem*
|
||||
binder_modem_create(
|
||||
RadioClient* client,
|
||||
const char* name,
|
||||
const char* path,
|
||||
const char* imei,
|
||||
const char* imeisv,
|
||||
const BinderSlotConfig* config,
|
||||
BinderRadio* radio,
|
||||
BinderNetwork* network,
|
||||
BinderSimCard* card,
|
||||
BinderData* data,
|
||||
BinderSimSettings* settings,
|
||||
struct ofono_cell_info* cell_info)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
#endif /* BINDER_MODEM_H */
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
1099
src/binder_netreg.c
Normal file
1099
src/binder_netreg.c
Normal file
File diff suppressed because it is too large
Load Diff
45
src/binder_netreg.h
Normal file
45
src/binder_netreg.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef BINDER_NETREG_H
|
||||
#define BINDER_NETREG_H
|
||||
|
||||
#include "binder_types.h"
|
||||
|
||||
#include <ofono/netreg.h>
|
||||
|
||||
void
|
||||
binder_netreg_init(void)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_netreg_cleanup(void)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
enum ofono_netreg_status
|
||||
binder_netreg_check_if_really_roaming(
|
||||
struct ofono_netreg* reg,
|
||||
enum ofono_netreg_status status)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
#endif /* BINDER_NETREG_H */
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
2288
src/binder_network.c
Normal file
2288
src/binder_network.c
Normal file
File diff suppressed because it is too large
Load Diff
137
src/binder_network.h
Normal file
137
src/binder_network.h
Normal file
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef BINDER_NETWORK_H
|
||||
#define BINDER_NETWORK_H
|
||||
|
||||
#include "binder_types.h"
|
||||
|
||||
#include <ofono/netreg.h>
|
||||
#include <ofono/radio-settings.h>
|
||||
|
||||
typedef enum binder_network_property {
|
||||
BINDER_NETWORK_PROPERTY_ANY,
|
||||
BINDER_NETWORK_PROPERTY_VOICE_STATE,
|
||||
BINDER_NETWORK_PROPERTY_DATA_STATE,
|
||||
BINDER_NETWORK_PROPERTY_MAX_DATA_CALLS,
|
||||
BINDER_NETWORK_PROPERTY_OPERATOR,
|
||||
BINDER_NETWORK_PROPERTY_PREF_MODES,
|
||||
BINDER_NETWORK_PROPERTY_ALLOWED_MODES,
|
||||
BINDER_NETWORK_PROPERTY_COUNT
|
||||
} BINDER_NETWORK_PROPERTY;
|
||||
|
||||
typedef struct binder_registration_state {
|
||||
enum ofono_netreg_status status;
|
||||
enum ofono_access_technology access_tech;
|
||||
RADIO_TECH radio_tech;
|
||||
gboolean em_enabled; /* TRUE is emergency calls are enabled */
|
||||
int lac;
|
||||
int ci;
|
||||
} BinderRegistrationState;
|
||||
|
||||
struct binder_network {
|
||||
BinderSimSettings* settings;
|
||||
BinderRegistrationState voice;
|
||||
BinderRegistrationState data;
|
||||
int max_data_calls;
|
||||
const struct ofono_network_operator* operator;
|
||||
enum ofono_radio_access_mode pref_modes; /* Mask */
|
||||
enum ofono_radio_access_mode allowed_modes; /* Mask */
|
||||
};
|
||||
|
||||
typedef
|
||||
void
|
||||
(*BinderNetworkPropertyFunc)(
|
||||
BinderNetwork* net,
|
||||
BINDER_NETWORK_PROPERTY property,
|
||||
void* user_data);
|
||||
|
||||
BinderNetwork*
|
||||
binder_network_new(
|
||||
const char* path,
|
||||
RadioClient* client,
|
||||
const char* log_prefix,
|
||||
BinderRadio* radio,
|
||||
BinderSimCard* sim_card,
|
||||
BinderSimSettings* settings,
|
||||
const BinderSlotConfig* config)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
BinderNetwork*
|
||||
binder_network_ref(
|
||||
BinderNetwork* net)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_network_unref(
|
||||
BinderNetwork* net)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_network_set_radio_caps(
|
||||
BinderNetwork* net,
|
||||
BinderRadioCaps* caps)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_network_set_allowed_modes(
|
||||
BinderNetwork* net,
|
||||
enum ofono_radio_access_mode modes,
|
||||
gboolean force_check)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
enum ofono_radio_access_mode
|
||||
binder_network_max_supported_mode(
|
||||
BinderNetwork* self)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_network_query_registration_state(
|
||||
BinderNetwork* net)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
gulong
|
||||
binder_network_add_property_handler(
|
||||
BinderNetwork* net,
|
||||
BINDER_NETWORK_PROPERTY property,
|
||||
BinderNetworkPropertyFunc callback,
|
||||
void* user_data)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_network_remove_handler(
|
||||
BinderNetwork* net,
|
||||
gulong id)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_network_remove_handlers(
|
||||
BinderNetwork* net,
|
||||
gulong* ids,
|
||||
int count)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
#define binder_network_remove_all_handlers(net, ids) \
|
||||
binder_network_remove_handlers(net, ids, G_N_ELEMENTS(ids))
|
||||
|
||||
#endif /* BINDER_NETWORK_H */
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
2184
src/binder_plugin.c
Normal file
2184
src/binder_plugin.c
Normal file
File diff suppressed because it is too large
Load Diff
556
src/binder_radio.c
Normal file
556
src/binder_radio.c
Normal file
@@ -0,0 +1,556 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "binder_base.h"
|
||||
#include "binder_log.h"
|
||||
#include "binder_radio.h"
|
||||
#include "binder_util.h"
|
||||
|
||||
#include <radio_client.h>
|
||||
#include <radio_request.h>
|
||||
#include <radio_request_group.h>
|
||||
|
||||
#include <gbinder_reader.h>
|
||||
#include <gbinder_writer.h>
|
||||
|
||||
#include <gutil_macros.h>
|
||||
#include <gutil_misc.h>
|
||||
|
||||
/*
|
||||
* Object states:
|
||||
*
|
||||
* 1. Idle (!pending && !retry)
|
||||
* 2. Power on/off request pending (pending)
|
||||
* 3. Power on retry has been scheduled (retry)
|
||||
*/
|
||||
typedef struct binder_radio_object {
|
||||
BinderBase base;
|
||||
BinderRadio pub;
|
||||
RadioClient* client;
|
||||
RadioRequestGroup* g;
|
||||
gulong state_event_id;
|
||||
char* log_prefix;
|
||||
GHashTable* req_table;
|
||||
RadioRequest* pending_req;
|
||||
guint retry_id;
|
||||
guint state_changed_while_request_pending;
|
||||
RADIO_STATE last_known_state;
|
||||
gboolean power_cycle;
|
||||
gboolean next_state_valid;
|
||||
gboolean next_state;
|
||||
} BinderRadioObject;
|
||||
|
||||
#define POWER_RETRY_SECS (1)
|
||||
|
||||
typedef BinderBaseClass BinderRadioObjectClass;
|
||||
GType binder_radio_object_get_type() BINDER_INTERNAL;
|
||||
G_DEFINE_TYPE(BinderRadioObject, binder_radio_object, BINDER_TYPE_BASE)
|
||||
#define PARENT_CLASS binder_radio_object_parent_class
|
||||
#define THIS_TYPE binder_radio_object_get_type()
|
||||
#define THIS(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, THIS_TYPE, BinderRadioObject)
|
||||
|
||||
#define DBG_(self,fmt,args...) DBG("%s" fmt, (self)->log_prefix, ##args)
|
||||
|
||||
/* Assumptions */
|
||||
BINDER_BASE_ASSERT_COUNT(BINDER_RADIO_PROPERTY_COUNT);
|
||||
|
||||
static
|
||||
void
|
||||
binder_radio_submit_power_request(
|
||||
BinderRadioObject* self,
|
||||
gboolean on);
|
||||
|
||||
static inline BinderRadioObject* binder_radio_object_cast(BinderRadio* net)
|
||||
{ return net ? THIS(G_CAST(net, BinderRadioObject, pub)) : NULL; }
|
||||
static inline void binder_radio_object_ref(BinderRadioObject* self)
|
||||
{ g_object_ref(self); }
|
||||
static inline void binder_radio_object_unref(BinderRadioObject* self)
|
||||
{ g_object_unref(self); }
|
||||
static inline gboolean binder_radio_state_off(RADIO_STATE state)
|
||||
{ return state == RADIO_STATE_OFF; }
|
||||
static inline gboolean binder_radio_state_on(RADIO_STATE state)
|
||||
{ return !binder_radio_state_off(state); }
|
||||
|
||||
static
|
||||
gboolean
|
||||
binder_radio_power_should_be_on(
|
||||
BinderRadioObject* self)
|
||||
{
|
||||
BinderRadio* radio = &self->pub;
|
||||
|
||||
return (radio->online || g_hash_table_size(self->req_table) > 0) &&
|
||||
!self->power_cycle;
|
||||
}
|
||||
|
||||
static
|
||||
gboolean
|
||||
binder_radio_power_request_retry_cb(
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderRadioObject* self = THIS(user_data);
|
||||
|
||||
DBG_(self, "");
|
||||
GASSERT(self->retry_id);
|
||||
self->retry_id = 0;
|
||||
binder_radio_submit_power_request(self,
|
||||
binder_radio_power_should_be_on(self));
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_radio_cancel_retry(
|
||||
BinderRadioObject* self)
|
||||
{
|
||||
if (self->retry_id) {
|
||||
DBG_(self, "retry cancelled");
|
||||
g_source_remove(self->retry_id);
|
||||
self->retry_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_radio_check_state(
|
||||
BinderRadioObject* self)
|
||||
{
|
||||
BinderRadio* radio = &self->pub;
|
||||
|
||||
if (!self->pending_req) {
|
||||
const gboolean should_be_on = binder_radio_power_should_be_on(self);
|
||||
|
||||
if (binder_radio_state_on(self->last_known_state) == should_be_on) {
|
||||
/* All is good, cancel pending retry if there is one */
|
||||
binder_radio_cancel_retry(self);
|
||||
} else if (self->state_changed_while_request_pending) {
|
||||
/* Hmm... BINDER's reaction was inadequate, repeat */
|
||||
binder_radio_submit_power_request(self, should_be_on);
|
||||
} else if (!self->retry_id) {
|
||||
/* There has been no reaction so far, wait a bit */
|
||||
DBG_(self, "retry scheduled");
|
||||
self->retry_id = g_timeout_add_seconds(POWER_RETRY_SECS,
|
||||
binder_radio_power_request_retry_cb, self);
|
||||
}
|
||||
}
|
||||
|
||||
/* Don't update public state while something is pending */
|
||||
if (!self->pending_req && !self->retry_id &&
|
||||
radio->state != self->last_known_state) {
|
||||
DBG_(self, "%s -> %s", binder_radio_state_string(radio->state),
|
||||
binder_radio_state_string(self->last_known_state));
|
||||
radio->state = self->last_known_state;
|
||||
binder_base_emit_property_change(&self->base,
|
||||
BINDER_RADIO_PROPERTY_STATE);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_radio_power_request_done(
|
||||
BinderRadioObject* self)
|
||||
{
|
||||
GASSERT(!self->pending_req);
|
||||
if (self->next_state_valid) {
|
||||
binder_radio_submit_power_request(self, self->next_state);
|
||||
} else {
|
||||
binder_radio_check_state(self);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_radio_power_request_cb(
|
||||
RadioRequest* req,
|
||||
RADIO_TX_STATUS status,
|
||||
RADIO_RESP resp,
|
||||
RADIO_ERROR error,
|
||||
const GBinderReader* args,
|
||||
void* user_data)
|
||||
{
|
||||
BinderRadioObject* self = THIS(user_data);
|
||||
|
||||
GASSERT(self->pending_req == req);
|
||||
radio_request_unref(self->pending_req);
|
||||
self->pending_req = NULL;
|
||||
|
||||
if (status == RADIO_TX_STATUS_OK) {
|
||||
if (resp != RADIO_RESP_SET_RADIO_POWER) {
|
||||
ofono_error("Unexpected setRadioPower response %d", resp);
|
||||
} else if (error != RADIO_ERROR_NONE) {
|
||||
ofono_error("Power request failed: %s",
|
||||
binder_radio_error_string(error));
|
||||
}
|
||||
} else {
|
||||
ofono_error("Power request failed");
|
||||
}
|
||||
|
||||
binder_radio_power_request_done(self);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_radio_submit_power_request(
|
||||
BinderRadioObject* self,
|
||||
gboolean on)
|
||||
{
|
||||
/* setRadioPower(int32 serial, bool on) */
|
||||
GBinderWriter writer;
|
||||
RadioRequest* req = radio_request_new(self->client,
|
||||
RADIO_REQ_SET_RADIO_POWER, &writer,
|
||||
binder_radio_power_request_cb, NULL, self);
|
||||
|
||||
gbinder_writer_append_bool(&writer, on);
|
||||
|
||||
self->next_state_valid = FALSE;
|
||||
self->next_state = on;
|
||||
self->state_changed_while_request_pending = 0;
|
||||
binder_radio_cancel_retry(self);
|
||||
|
||||
GASSERT(!self->pending_req);
|
||||
radio_request_set_blocking(req, TRUE);
|
||||
if (radio_request_submit(req)) {
|
||||
self->pending_req = req; /* Keep the ref */
|
||||
} else {
|
||||
radio_request_unref(req);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_radio_power_request(
|
||||
BinderRadioObject* self,
|
||||
gboolean on,
|
||||
gboolean allow_repeat)
|
||||
{
|
||||
const char* on_off = on ? "on" : "off";
|
||||
|
||||
if (self->pending_req) {
|
||||
if (allow_repeat || self->next_state != on) {
|
||||
/* Wait for the pending request to complete */
|
||||
self->next_state_valid = TRUE;
|
||||
self->next_state = on;
|
||||
DBG_(self, "%s (queued)", on_off);
|
||||
} else {
|
||||
DBG_(self, "%s (ignored)", on_off);
|
||||
}
|
||||
} else {
|
||||
if (binder_radio_state_on(self->last_known_state) == on) {
|
||||
DBG_(self, "%s (already)", on_off);
|
||||
binder_radio_check_state(self);
|
||||
} else {
|
||||
DBG_(self, "%s", on_off);
|
||||
binder_radio_submit_power_request(self, on);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_radio_state_changed(
|
||||
RadioClient* client,
|
||||
RADIO_IND code,
|
||||
const GBinderReader* args,
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderRadioObject* self = THIS(user_data);
|
||||
RADIO_STATE radio_state = RADIO_STATE_UNAVAILABLE;
|
||||
GBinderReader reader;
|
||||
gint32 tmp;
|
||||
|
||||
/* radioStateChanged(RadioIndicationType, RadioState radioState); */
|
||||
gbinder_reader_copy(&reader, args);
|
||||
if (gbinder_reader_read_int32(&reader, &tmp)) {
|
||||
radio_state = tmp;
|
||||
} else {
|
||||
ofono_error("Failed to parse radioStateChanged payload");
|
||||
}
|
||||
|
||||
if (radio_state != RADIO_STATE_UNAVAILABLE) {
|
||||
DBG_(self, "%s", binder_radio_state_string(radio_state));
|
||||
|
||||
GASSERT(!self->pending_req || !self->retry_id);
|
||||
if (self->power_cycle && binder_radio_state_off(radio_state)) {
|
||||
DBG_(self, "switched off for power cycle");
|
||||
self->power_cycle = FALSE;
|
||||
}
|
||||
|
||||
self->last_known_state = radio_state;
|
||||
|
||||
if (self->pending_req) {
|
||||
if (binder_radio_state_on(radio_state) ==
|
||||
binder_radio_power_should_be_on(self)) {
|
||||
DBG_(self, "dropping pending request");
|
||||
/*
|
||||
* All right, the modem has switched to the
|
||||
* desired state, drop the request.
|
||||
*/
|
||||
radio_request_drop(self->pending_req);
|
||||
self->pending_req = NULL;
|
||||
binder_radio_power_request_done(self);
|
||||
|
||||
/* We are done */
|
||||
return;
|
||||
} else {
|
||||
/* Something weird is going on */
|
||||
self->state_changed_while_request_pending++;
|
||||
}
|
||||
}
|
||||
|
||||
binder_radio_check_state(self);
|
||||
}
|
||||
}
|
||||
|
||||
/*==========================================================================*
|
||||
* API
|
||||
*==========================================================================*/
|
||||
|
||||
BinderRadio*
|
||||
binder_radio_new(
|
||||
RadioClient* client,
|
||||
const char* log_prefix)
|
||||
{
|
||||
BinderRadioObject* self = g_object_new(THIS_TYPE, NULL);
|
||||
BinderRadio* radio = &self->pub;
|
||||
|
||||
self->client = radio_client_ref(client);
|
||||
self->g = radio_request_group_new(client);
|
||||
self->log_prefix = binder_dup_prefix(log_prefix);
|
||||
DBG_(self, "");
|
||||
|
||||
self->state_event_id = radio_client_add_indication_handler(client,
|
||||
RADIO_IND_RADIO_STATE_CHANGED, binder_radio_state_changed, self);
|
||||
|
||||
/*
|
||||
* Some modem adaptations like to receive power off request at startup
|
||||
* even if radio is already off. Make those happy.
|
||||
*/
|
||||
binder_radio_submit_power_request(self, FALSE);
|
||||
return radio;
|
||||
}
|
||||
|
||||
BinderRadio*
|
||||
binder_radio_ref(
|
||||
BinderRadio* radio)
|
||||
{
|
||||
BinderRadioObject* self = binder_radio_object_cast(radio);
|
||||
|
||||
if (G_LIKELY(self)) {
|
||||
binder_radio_object_ref(self);
|
||||
return radio;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
binder_radio_unref(
|
||||
BinderRadio* radio)
|
||||
{
|
||||
BinderRadioObject* self = binder_radio_object_cast(radio);
|
||||
|
||||
if (G_LIKELY(self)) {
|
||||
binder_radio_object_unref(self);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
binder_radio_set_online(
|
||||
BinderRadio* radio,
|
||||
gboolean online)
|
||||
{
|
||||
BinderRadioObject* self = binder_radio_object_cast(radio);
|
||||
|
||||
if (G_LIKELY(self) && radio->online != online) {
|
||||
gboolean on, was_on = binder_radio_power_should_be_on(self);
|
||||
|
||||
radio->online = online;
|
||||
on = binder_radio_power_should_be_on(self);
|
||||
if (was_on != on) {
|
||||
binder_radio_power_request(self, on, FALSE);
|
||||
}
|
||||
binder_base_emit_property_change(&self->base,
|
||||
BINDER_RADIO_PROPERTY_ONLINE);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
binder_radio_confirm_power_on(
|
||||
BinderRadio* radio)
|
||||
{
|
||||
BinderRadioObject* self = binder_radio_object_cast(radio);
|
||||
|
||||
if (G_LIKELY(self) && binder_radio_power_should_be_on(self)) {
|
||||
if (self->pending_req) {
|
||||
if (!self->next_state) {
|
||||
/* Wait for the pending request to complete */
|
||||
self->next_state_valid = TRUE;
|
||||
self->next_state = TRUE;
|
||||
DBG_(self, "on (queued)");
|
||||
}
|
||||
} else {
|
||||
DBG_(self, "on");
|
||||
binder_radio_submit_power_request(self, TRUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
binder_radio_power_cycle(
|
||||
BinderRadio* radio)
|
||||
{
|
||||
BinderRadioObject* self = binder_radio_object_cast(radio);
|
||||
|
||||
if (G_LIKELY(self)) {
|
||||
if (binder_radio_state_off(self->last_known_state)) {
|
||||
DBG_(self, "power is already off");
|
||||
GASSERT(!self->power_cycle);
|
||||
} else if (self->power_cycle) {
|
||||
DBG_(self, "already in progress");
|
||||
} else {
|
||||
DBG_(self, "initiated");
|
||||
self->power_cycle = TRUE;
|
||||
if (!self->pending_req) {
|
||||
binder_radio_submit_power_request(self, FALSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
binder_radio_power_on(
|
||||
BinderRadio* radio,
|
||||
gpointer tag)
|
||||
{
|
||||
BinderRadioObject* self = binder_radio_object_cast(radio);
|
||||
|
||||
if (G_LIKELY(self)) {
|
||||
if (!g_hash_table_contains(self->req_table, tag)) {
|
||||
const gboolean was_on = binder_radio_power_should_be_on(self);
|
||||
|
||||
DBG_(self, "%p", tag);
|
||||
g_hash_table_insert(self->req_table, tag, tag);
|
||||
if (!was_on && binder_radio_power_should_be_on(self)) {
|
||||
binder_radio_power_request(self, TRUE, FALSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
binder_radio_power_off(
|
||||
BinderRadio* radio,
|
||||
gpointer tag)
|
||||
{
|
||||
BinderRadioObject* self = binder_radio_object_cast(radio);
|
||||
|
||||
if (G_LIKELY(self)) {
|
||||
if (g_hash_table_remove(self->req_table, tag)) {
|
||||
DBG_(self, "%p", tag);
|
||||
if (!binder_radio_power_should_be_on(self)) {
|
||||
/* The last one turns the lights off */
|
||||
binder_radio_power_request(self, FALSE, FALSE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gulong
|
||||
binder_radio_add_property_handler(
|
||||
BinderRadio* radio,
|
||||
BINDER_RADIO_PROPERTY property,
|
||||
BinderRadioPropertyFunc callback,
|
||||
void* user_data)
|
||||
{
|
||||
BinderRadioObject* self = binder_radio_object_cast(radio);
|
||||
|
||||
return G_LIKELY(self) ? binder_base_add_property_handler(&self->base,
|
||||
property, G_CALLBACK(callback), user_data) : 0;
|
||||
}
|
||||
|
||||
void
|
||||
binder_radio_remove_handler(
|
||||
BinderRadio* radio,
|
||||
gulong id)
|
||||
{
|
||||
if (G_LIKELY(id)) {
|
||||
BinderRadioObject* self = binder_radio_object_cast(radio);
|
||||
|
||||
if (G_LIKELY(self)) {
|
||||
g_signal_handler_disconnect(self, id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
binder_radio_remove_handlers(
|
||||
BinderRadio* radio,
|
||||
gulong* ids,
|
||||
int count)
|
||||
{
|
||||
gutil_disconnect_handlers(binder_radio_object_cast(radio), ids, count);
|
||||
}
|
||||
|
||||
/*==========================================================================*
|
||||
* Internals
|
||||
*==========================================================================*/
|
||||
|
||||
static
|
||||
void
|
||||
binder_radio_object_init(
|
||||
BinderRadioObject* self)
|
||||
{
|
||||
self->req_table = g_hash_table_new(g_direct_hash, g_direct_equal);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_radio_object_finalize(
|
||||
GObject* object)
|
||||
{
|
||||
BinderRadioObject* self = THIS(object);
|
||||
|
||||
DBG_(self, "");
|
||||
binder_radio_cancel_retry(self);
|
||||
|
||||
radio_request_drop(self->pending_req);
|
||||
radio_request_group_cancel(self->g);
|
||||
radio_request_group_unref(self->g);
|
||||
radio_client_remove_handler(self->client, self->state_event_id);
|
||||
radio_client_unref(self->client);
|
||||
|
||||
g_hash_table_unref(self->req_table);
|
||||
g_free(self->log_prefix);
|
||||
|
||||
G_OBJECT_CLASS(PARENT_CLASS)->finalize(object);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_radio_object_class_init(
|
||||
BinderRadioObjectClass* klass)
|
||||
{
|
||||
G_OBJECT_CLASS(klass)->finalize = binder_radio_object_finalize;
|
||||
BINDER_BASE_CLASS(klass)->public_offset =
|
||||
G_STRUCT_OFFSET(BinderRadioObject, pub);
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
116
src/binder_radio.h
Normal file
116
src/binder_radio.h
Normal file
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef BINDER_RADIO_H
|
||||
#define BINDER_RADIO_H
|
||||
|
||||
#include "binder_types.h"
|
||||
|
||||
typedef enum binder_radio_property {
|
||||
BINDER_RADIO_PROPERTY_ANY,
|
||||
BINDER_RADIO_PROPERTY_STATE,
|
||||
BINDER_RADIO_PROPERTY_ONLINE,
|
||||
BINDER_RADIO_PROPERTY_COUNT
|
||||
} BINDER_RADIO_PROPERTY;
|
||||
|
||||
struct binder_radio {
|
||||
RADIO_STATE state;
|
||||
gboolean online;
|
||||
};
|
||||
|
||||
typedef
|
||||
void
|
||||
(*BinderRadioPropertyFunc)(
|
||||
BinderRadio* radio,
|
||||
BINDER_RADIO_PROPERTY property,
|
||||
void* user_data);
|
||||
|
||||
BinderRadio*
|
||||
binder_radio_new(
|
||||
RadioClient* client,
|
||||
const char* log_prefix)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
BinderRadio*
|
||||
binder_radio_ref(
|
||||
BinderRadio* radio)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_radio_unref(
|
||||
BinderRadio* radio)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_radio_power_on(
|
||||
BinderRadio* radio,
|
||||
gpointer tag)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_radio_power_off(
|
||||
BinderRadio* radio,
|
||||
gpointer tag)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_radio_power_cycle(
|
||||
BinderRadio* radio)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_radio_confirm_power_on(
|
||||
BinderRadio* radio)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_radio_set_online(
|
||||
BinderRadio* radio,
|
||||
gboolean online)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
gulong
|
||||
binder_radio_add_property_handler(
|
||||
BinderRadio* net,
|
||||
BINDER_RADIO_PROPERTY property,
|
||||
BinderRadioPropertyFunc callback,
|
||||
void* user_data)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_radio_remove_handler(
|
||||
BinderRadio* radio,
|
||||
gulong id)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_radio_remove_handlers(
|
||||
BinderRadio* radio,
|
||||
gulong* ids,
|
||||
int count)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
#define binder_radio_remove_all_handlers(r,ids) \
|
||||
binder_radio_remove_handlers(r, ids, G_N_ELEMENTS(ids))
|
||||
|
||||
#endif /* BINDER_RADIO_H */
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
2030
src/binder_radio_caps.c
Normal file
2030
src/binder_radio_caps.c
Normal file
File diff suppressed because it is too large
Load Diff
171
src/binder_radio_caps.h
Normal file
171
src/binder_radio_caps.h
Normal file
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef BINDER_RADIO_CAPS_H
|
||||
#define BINDER_RADIO_CAPS_H
|
||||
|
||||
#include "binder_types.h"
|
||||
|
||||
#include <ofono/radio-settings.h>
|
||||
#include <ofono/slot.h>
|
||||
|
||||
struct ofono_watch;
|
||||
|
||||
typedef
|
||||
void
|
||||
(*BinderRadioCapsFunc)(
|
||||
BinderRadioCaps* caps,
|
||||
void* user_data);
|
||||
|
||||
typedef
|
||||
void
|
||||
(*BinderRadioCapsManagerFunc)(
|
||||
BinderRadioCapsManager* mgr,
|
||||
void* user_data);
|
||||
|
||||
/* RadioCapability pointer is NULL if functionality is unsupported */
|
||||
typedef
|
||||
void
|
||||
(*BinderRadioCapsCheckFunc)(
|
||||
const RadioCapability* cap,
|
||||
void* user_data);
|
||||
|
||||
/* Caller must unref the request */
|
||||
RadioRequest*
|
||||
binder_radio_caps_check(
|
||||
RadioClient* client,
|
||||
BinderRadioCapsCheckFunc cb,
|
||||
void* user_data)
|
||||
G_GNUC_WARN_UNUSED_RESULT
|
||||
BINDER_INTERNAL;
|
||||
|
||||
/* There must be a single BinderRadioCapsManager shared by all modems */
|
||||
BinderRadioCapsManager*
|
||||
binder_radio_caps_manager_new(
|
||||
BinderDataManager* data)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
BinderRadioCapsManager*
|
||||
binder_radio_caps_manager_ref(
|
||||
BinderRadioCapsManager* mgr)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_radio_caps_manager_unref(
|
||||
BinderRadioCapsManager* mgr)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
gulong
|
||||
binder_radio_caps_manager_add_tx_aborted_handler(
|
||||
BinderRadioCapsManager* mgr,
|
||||
BinderRadioCapsManagerFunc cb,
|
||||
void* user_data)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
gulong
|
||||
binder_radio_caps_manager_add_tx_done_handler(
|
||||
BinderRadioCapsManager* mgr,
|
||||
BinderRadioCapsManagerFunc cb,
|
||||
void* user_data)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_radio_caps_manager_remove_handler(
|
||||
BinderRadioCapsManager* mgr,
|
||||
gulong id)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_radio_caps_manager_remove_handlers(
|
||||
BinderRadioCapsManager* mgr,
|
||||
gulong* ids,
|
||||
int count)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
#define binder_radio_caps_manager_remove_all_handlers(mgr, ids) \
|
||||
binder_radio_caps_manager_remove_handlers(mgr, ids, G_N_ELEMENTS(ids))
|
||||
|
||||
/* And one BinderRadioCaps object per modem */
|
||||
|
||||
struct binder_radio_caps {
|
||||
BinderRadioCapsManager* mgr;
|
||||
RADIO_ACCESS_FAMILY raf;
|
||||
};
|
||||
|
||||
BinderRadioCaps*
|
||||
binder_radio_caps_new(
|
||||
BinderRadioCapsManager* mgr,
|
||||
const char* log_prefix,
|
||||
RadioClient* client,
|
||||
struct ofono_watch* watch,
|
||||
BinderData* data,
|
||||
BinderRadio* radio,
|
||||
BinderSimCard* sim,
|
||||
BinderSimSettings* settings,
|
||||
const BinderSlotConfig* config,
|
||||
const RadioCapability* cap)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
BinderRadioCaps*
|
||||
binder_radio_caps_ref(
|
||||
BinderRadioCaps* caps)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_radio_caps_unref(
|
||||
BinderRadioCaps* caps)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_radio_caps_drop(
|
||||
BinderRadioCaps* caps)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
gulong
|
||||
binder_radio_caps_add_raf_handler(
|
||||
BinderRadioCaps* caps,
|
||||
BinderRadioCapsFunc cb,
|
||||
void* user_data)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_radio_caps_remove_handler(
|
||||
BinderRadioCaps* caps,
|
||||
gulong id)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
/* Data requests */
|
||||
|
||||
BinderRadioCapsRequest*
|
||||
binder_radio_caps_request_new(
|
||||
BinderRadioCaps* caps,
|
||||
enum ofono_radio_access_mode modes, /* Mask */
|
||||
enum ofono_slot_data_role role)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_radio_caps_request_free(
|
||||
BinderRadioCapsRequest* req)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
#endif /* BINDER_RADIO_CAPS_H */
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
251
src/binder_radio_settings.c
Normal file
251
src/binder_radio_settings.c
Normal file
@@ -0,0 +1,251 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "binder_modem.h"
|
||||
#include "binder_radio_settings.h"
|
||||
#include "binder_sim_settings.h"
|
||||
#include "binder_util.h"
|
||||
#include "binder_log.h"
|
||||
|
||||
#include <ofono/radio-settings.h>
|
||||
|
||||
typedef struct binder_radio_settings {
|
||||
struct ofono_radio_settings* rs;
|
||||
BinderSimSettings* settings;
|
||||
char* log_prefix;
|
||||
guint callback_id;
|
||||
} BinderRadioSettings;
|
||||
|
||||
typedef struct binder_radio_settings_cbd {
|
||||
BinderRadioSettings* self;
|
||||
union _ofono_radio_settings_cb {
|
||||
ofono_radio_settings_rat_mode_set_cb_t rat_mode_set;
|
||||
ofono_radio_settings_rat_mode_query_cb_t rat_mode_query;
|
||||
ofono_radio_settings_available_rats_query_cb_t available_rats;
|
||||
BinderCallback ptr;
|
||||
} cb;
|
||||
gpointer data;
|
||||
} BinderRadioSettingsCbData;
|
||||
|
||||
#define DBG_(self,fmt,args...) DBG("%s" fmt, (self)->log_prefix, ##args)
|
||||
|
||||
static inline BinderRadioSettings*
|
||||
binder_radio_settings_get_data(struct ofono_radio_settings* rs)
|
||||
{ return ofono_radio_settings_get_data(rs); }
|
||||
|
||||
static
|
||||
void
|
||||
binder_radio_settings_callback_data_free(
|
||||
gpointer cbd)
|
||||
{
|
||||
g_slice_free(BinderRadioSettingsCbData, cbd);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_radio_settings_later(
|
||||
BinderRadioSettings* self,
|
||||
GSourceFunc fn,
|
||||
BinderCallback cb,
|
||||
void* data)
|
||||
{
|
||||
BinderRadioSettingsCbData* cbd = g_slice_new0(BinderRadioSettingsCbData);
|
||||
|
||||
cbd->self = self;
|
||||
cbd->cb.ptr = cb;
|
||||
cbd->data = data;
|
||||
|
||||
GASSERT(!self->callback_id);
|
||||
self->callback_id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, fn, cbd,
|
||||
binder_radio_settings_callback_data_free);
|
||||
}
|
||||
|
||||
static
|
||||
gboolean
|
||||
binder_radio_settings_set_rat_mode_cb(
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderRadioSettingsCbData* cbd = user_data;
|
||||
BinderRadioSettings* self = cbd->self;
|
||||
struct ofono_error error;
|
||||
|
||||
GASSERT(self->callback_id);
|
||||
self->callback_id = 0;
|
||||
cbd->cb.rat_mode_set(binder_error_ok(&error), cbd->data);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_radio_settings_set_rat_mode(
|
||||
struct ofono_radio_settings* rs,
|
||||
enum ofono_radio_access_mode mode,
|
||||
ofono_radio_settings_rat_mode_set_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
BinderRadioSettings* self = binder_radio_settings_get_data(rs);
|
||||
|
||||
DBG_(self, "%s", ofono_radio_access_mode_to_string(mode));
|
||||
binder_sim_settings_set_pref(self->settings,
|
||||
binder_access_modes_up_to(mode));
|
||||
binder_radio_settings_later(self,
|
||||
binder_radio_settings_set_rat_mode_cb, BINDER_CB(cb), data);
|
||||
}
|
||||
|
||||
static
|
||||
gboolean
|
||||
binder_radio_settings_query_rat_mode_cb(
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderRadioSettingsCbData* cbd = user_data;
|
||||
BinderRadioSettings* self = cbd->self;
|
||||
const enum ofono_radio_access_mode mode =
|
||||
ofono_radio_access_max_mode(self->settings->pref);
|
||||
struct ofono_error error;
|
||||
|
||||
DBG_(self, "rat mode %s", ofono_radio_access_mode_to_string(mode));
|
||||
GASSERT(self->callback_id);
|
||||
self->callback_id = 0;
|
||||
cbd->cb.rat_mode_query(binder_error_ok(&error), mode, cbd->data);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_radio_settings_query_rat_mode(
|
||||
struct ofono_radio_settings* rs,
|
||||
ofono_radio_settings_rat_mode_query_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
BinderRadioSettings* self = binder_radio_settings_get_data(rs);
|
||||
|
||||
DBG_(self, "");
|
||||
binder_radio_settings_later(self,
|
||||
binder_radio_settings_query_rat_mode_cb, BINDER_CB(cb), data);
|
||||
}
|
||||
|
||||
static
|
||||
gboolean
|
||||
binder_radio_settings_query_available_rats_cb(
|
||||
gpointer data)
|
||||
{
|
||||
BinderRadioSettingsCbData* cbd = data;
|
||||
BinderRadioSettings* self = cbd->self;
|
||||
struct ofono_error error;
|
||||
|
||||
GASSERT(self->callback_id);
|
||||
self->callback_id = 0;
|
||||
cbd->cb.available_rats(binder_error_ok(&error),
|
||||
self->settings->techs, cbd->data);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_radio_settings_query_available_rats(
|
||||
struct ofono_radio_settings* rs,
|
||||
ofono_radio_settings_available_rats_query_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
BinderRadioSettings* self = binder_radio_settings_get_data(rs);
|
||||
|
||||
DBG_(self, "");
|
||||
binder_radio_settings_later(self,
|
||||
binder_radio_settings_query_available_rats_cb, BINDER_CB(cb), data);
|
||||
}
|
||||
|
||||
static
|
||||
gboolean
|
||||
binder_radio_settings_register(
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderRadioSettings* self = user_data;
|
||||
|
||||
GASSERT(self->callback_id);
|
||||
self->callback_id = 0;
|
||||
ofono_radio_settings_register(self->rs);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
binder_radio_settings_probe(
|
||||
struct ofono_radio_settings* rs,
|
||||
unsigned int vendor,
|
||||
void* data)
|
||||
{
|
||||
BinderModem* modem = binder_modem_get_data(data);
|
||||
BinderRadioSettings* self = g_new0(struct binder_radio_settings, 1);
|
||||
|
||||
self->rs = rs;
|
||||
self->log_prefix = binder_dup_prefix(modem->log_prefix);
|
||||
self->settings = binder_sim_settings_ref(modem->sim_settings);
|
||||
self->callback_id = g_idle_add(binder_radio_settings_register, self);
|
||||
|
||||
DBG_(self, "");
|
||||
ofono_radio_settings_set_data(rs, self);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_radio_settings_remove(
|
||||
struct ofono_radio_settings* rs)
|
||||
{
|
||||
BinderRadioSettings* self = binder_radio_settings_get_data(rs);
|
||||
|
||||
DBG_(self, "");
|
||||
if (self->callback_id) {
|
||||
g_source_remove(self->callback_id);
|
||||
}
|
||||
binder_sim_settings_unref(self->settings);
|
||||
g_free(self->log_prefix);
|
||||
g_free(self);
|
||||
|
||||
ofono_radio_settings_set_data(rs, NULL);
|
||||
}
|
||||
|
||||
/*==========================================================================*
|
||||
* API
|
||||
*==========================================================================*/
|
||||
|
||||
static const struct ofono_radio_settings_driver binder_radio_settings_driver = {
|
||||
.name = BINDER_DRIVER,
|
||||
.probe = binder_radio_settings_probe,
|
||||
.remove = binder_radio_settings_remove,
|
||||
.query_rat_mode = binder_radio_settings_query_rat_mode,
|
||||
.set_rat_mode = binder_radio_settings_set_rat_mode,
|
||||
.query_available_rats = binder_radio_settings_query_available_rats
|
||||
};
|
||||
|
||||
void
|
||||
binder_radio_settings_init()
|
||||
{
|
||||
ofono_radio_settings_driver_register(&binder_radio_settings_driver);
|
||||
}
|
||||
|
||||
void
|
||||
binder_radio_settings_cleanup()
|
||||
{
|
||||
ofono_radio_settings_driver_unregister(&binder_radio_settings_driver);
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
37
src/binder_radio_settings.h
Normal file
37
src/binder_radio_settings.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef BINDER_RADIO_SETTINGS_H
|
||||
#define BINDER_RADIO_SETTINGS_H
|
||||
|
||||
#include "binder_types.h"
|
||||
|
||||
void
|
||||
binder_radio_settings_init(void)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_radio_settings_cleanup(void)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
#endif /* BINDER_RADIO_SETTINGS_H */
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
2564
src/binder_sim.c
Normal file
2564
src/binder_sim.c
Normal file
File diff suppressed because it is too large
Load Diff
37
src/binder_sim.h
Normal file
37
src/binder_sim.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef BINDER_SIM_H
|
||||
#define BINDER_SIM_H
|
||||
|
||||
#include "binder_types.h"
|
||||
|
||||
void
|
||||
binder_sim_init(void)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_sim_cleanup(void)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
#endif /* BINDER_SIM_H */
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
870
src/binder_sim_card.c
Normal file
870
src/binder_sim_card.c
Normal file
@@ -0,0 +1,870 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "binder_sim_card.h"
|
||||
#include "binder_radio.h"
|
||||
#include "binder_util.h"
|
||||
#include "binder_log.h"
|
||||
|
||||
#include <radio_client.h>
|
||||
#include <radio_request.h>
|
||||
#include <radio_request_group.h>
|
||||
|
||||
#include <gbinder_reader.h>
|
||||
#include <gbinder_writer.h>
|
||||
|
||||
#include <gutil_misc.h>
|
||||
|
||||
/*
|
||||
* First we wait for USIM app to get activated by itself. If that
|
||||
* doesn't happen within UICC_SUBSCRIPTION_START_MS we poke the SIM
|
||||
* with SET_UICC_SUBSCRIPTION request, resubmitting it if it times out.
|
||||
* If nothing happens within UICC_SUBSCRIPTION_TIMEOUT_MS we give up.
|
||||
*
|
||||
* Submitting SET_UICC_SUBSCRIPTION request when modem doesn't expect
|
||||
* it sometimes breaks pretty much everything. Unfortunately, there no
|
||||
* reliable way to find out when modem expects it and when it doesn't :/
|
||||
*/
|
||||
#define UICC_SUBSCRIPTION_RETRY_MS (500)
|
||||
#define UICC_SUBSCRIPTION_START_MS (5000)
|
||||
#define UICC_SUBSCRIPTION_TIMEOUT_MS (30000)
|
||||
|
||||
/* SIM I/O idle timeout is measured in the number of idle loops.
|
||||
* When active SIM I/O is going on, the idle loop count very rarely
|
||||
* exceeds 1 between the requests, so 10 is more than enough. Idle
|
||||
* loop is actually more accurate criteria than a timeout because
|
||||
* it doesn't depend that much on the system load. */
|
||||
#define SIM_IO_IDLE_LOOPS (10)
|
||||
|
||||
enum binder_sim_card_event {
|
||||
EVENT_SIM_STATUS_CHANGED,
|
||||
EVENT_UICC_SUBSCRIPTION_STATUS_CHANGED,
|
||||
EVENT_COUNT
|
||||
};
|
||||
|
||||
typedef struct binder_sim_card_object {
|
||||
BinderSimCard card;
|
||||
RadioRequest* status_req;
|
||||
RadioRequest* sub_req;
|
||||
RadioRequestGroup* g;
|
||||
guint sub_start_timer;
|
||||
gulong event_id[EVENT_COUNT];
|
||||
guint sim_io_idle_id;
|
||||
guint sim_io_idle_count;
|
||||
GHashTable* sim_io_pending;
|
||||
} BinderSimCardObject;
|
||||
|
||||
enum binder_sim_card_signal {
|
||||
SIGNAL_STATUS_RECEIVED,
|
||||
SIGNAL_STATUS_CHANGED,
|
||||
SIGNAL_STATE_CHANGED,
|
||||
SIGNAL_APP_CHANGED,
|
||||
SIGNAL_SIM_IO_ACTIVE_CHANGED,
|
||||
SIGNAL_COUNT
|
||||
};
|
||||
|
||||
#define SIGNAL_STATUS_RECEIVED_NAME "binder-simcard-status-received"
|
||||
#define SIGNAL_STATUS_CHANGED_NAME "binder-simcard-status-changed"
|
||||
#define SIGNAL_STATE_CHANGED_NAME "binder-simcard-state-changed"
|
||||
#define SIGNAL_APP_CHANGED_NAME "binder-simcard-app-changed"
|
||||
#define SIGNAL_SIM_IO_ACTIVE_CHANGED_NAME "binder-simcard-sim-io-active-changed"
|
||||
|
||||
static guint binder_sim_card_signals[SIGNAL_COUNT] = { 0 };
|
||||
|
||||
typedef GObjectClass BinderSimCardObjectClass;
|
||||
GType binder_sim_card_get_type() BINDER_INTERNAL;
|
||||
G_DEFINE_TYPE(BinderSimCardObject, binder_sim_card, G_TYPE_OBJECT)
|
||||
#define PARENT_CLASS binder_sim_card_parent_class
|
||||
#define THIS_TYPE binder_sim_card_get_type()
|
||||
#define THIS(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, THIS_TYPE, \
|
||||
BinderSimCardObject)
|
||||
|
||||
#define NEW_SIGNAL(klass,name) NEW_SIGNAL_(klass,name##_CHANGED)
|
||||
#define NEW_SIGNAL_(klass,name) \
|
||||
binder_sim_card_signals[SIGNAL_##name] = \
|
||||
g_signal_new(SIGNAL_##name##_NAME, \
|
||||
G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_FIRST, \
|
||||
0, NULL, NULL, NULL, G_TYPE_NONE, 0)
|
||||
|
||||
#define BINDER_SIMCARD_STATE_CHANGED (0x01)
|
||||
#define BINDER_SIMCARD_STATUS_CHANGED (0x02)
|
||||
|
||||
static inline BinderSimCardObject* binder_sim_card_cast(BinderSimCard* card)
|
||||
{ return G_LIKELY(card) ? THIS(card) : NULL; }
|
||||
|
||||
static
|
||||
gboolean
|
||||
binder_sim_card_app_equal(
|
||||
const BinderSimCardApp* a1,
|
||||
const BinderSimCardApp* a2)
|
||||
{
|
||||
if (a1 == a2) {
|
||||
return TRUE;
|
||||
} else if (!a1 || !a2) {
|
||||
return FALSE;
|
||||
} else {
|
||||
return a1->app_type == a2->app_type &&
|
||||
a1->app_state == a2->app_state &&
|
||||
a1->perso_substate == a2->perso_substate &&
|
||||
a1->pin_replaced == a2->pin_replaced &&
|
||||
a1->pin1_state == a2->pin1_state &&
|
||||
a1->pin2_state == a2->pin2_state &&
|
||||
!g_strcmp0(a1->aid, a2->aid) &&
|
||||
!g_strcmp0(a1->label, a2->label);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
binder_sim_card_status_compare(
|
||||
const BinderSimCardStatus* s1,
|
||||
const BinderSimCardStatus* s2)
|
||||
{
|
||||
if (s1 == s2) {
|
||||
return 0;
|
||||
} else if (!s1 || !s2) {
|
||||
return BINDER_SIMCARD_STATE_CHANGED | BINDER_SIMCARD_STATUS_CHANGED;
|
||||
} else {
|
||||
int diff = 0;
|
||||
|
||||
if (s1->card_state != s2->card_state) {
|
||||
diff |= BINDER_SIMCARD_STATE_CHANGED;
|
||||
}
|
||||
|
||||
if (s1->pin_state != s2->pin_state ||
|
||||
s1->gsm_umts_index != s2->gsm_umts_index ||
|
||||
s1->ims_index != s2->ims_index ||
|
||||
s1->num_apps != s2->num_apps) {
|
||||
diff |= BINDER_SIMCARD_STATUS_CHANGED;
|
||||
} else {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < s1->num_apps; i++) {
|
||||
if (!binder_sim_card_app_equal(s1->apps + i, s2->apps + i)) {
|
||||
diff |= BINDER_SIMCARD_STATUS_CHANGED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return diff;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_sim_card_status_free(
|
||||
BinderSimCardStatus* status)
|
||||
{
|
||||
if (status) {
|
||||
if (status->apps) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < status->num_apps; i++) {
|
||||
g_free(status->apps[i].aid);
|
||||
g_free(status->apps[i].label);
|
||||
}
|
||||
}
|
||||
/* status->apps is allocated from the same memory block */
|
||||
g_free(status);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_sim_card_tx_start(
|
||||
BinderSimCardObject* self)
|
||||
{
|
||||
RADIO_BLOCK block = radio_request_group_block_status(self->g);
|
||||
|
||||
if (block == RADIO_BLOCK_NONE) {
|
||||
block = radio_request_group_block(self->g);
|
||||
DBG("status tx for slot %u %s", self->card.slot,
|
||||
(block == RADIO_BLOCK_ACQUIRED) ? "started" : "starting");
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_sim_card_tx_check(
|
||||
BinderSimCardObject* self)
|
||||
{
|
||||
if (radio_request_group_block_status(self->g) != RADIO_BLOCK_NONE) {
|
||||
BinderSimCard* card = &self->card;
|
||||
const BinderSimCardStatus* status = card->status;
|
||||
|
||||
if (status && status->card_state == RADIO_CARD_STATE_PRESENT) {
|
||||
/*
|
||||
* Transaction (if there is any) is finished when
|
||||
* both GET_SIM_STATUS and SET_UICC_SUBSCRIPTION
|
||||
* complete or get dropped.
|
||||
*/
|
||||
if (!self->status_req && !self->sub_req &&
|
||||
status->gsm_umts_index >= 0 &&
|
||||
status->gsm_umts_index < status->num_apps) {
|
||||
DBG("status tx for slot %u finished", card->slot);
|
||||
radio_request_group_unblock(self->g);
|
||||
}
|
||||
} else {
|
||||
DBG("status tx for slot %u cancelled", card->slot);
|
||||
radio_request_group_unblock(self->g);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_sim_card_subscription_done(
|
||||
BinderSimCardObject* self)
|
||||
{
|
||||
if (self->sub_start_timer) {
|
||||
/* Don't need this timer anymore */
|
||||
g_source_remove(self->sub_start_timer);
|
||||
self->sub_start_timer = 0;
|
||||
}
|
||||
if (self->sub_req) {
|
||||
radio_request_drop(self->sub_req);
|
||||
self->sub_req = NULL;
|
||||
}
|
||||
binder_sim_card_tx_check(self);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_sim_card_subscribe_cb(
|
||||
RadioRequest* req,
|
||||
RADIO_TX_STATUS status,
|
||||
RADIO_RESP resp,
|
||||
RADIO_ERROR error,
|
||||
const GBinderReader* args,
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderSimCardObject* self = THIS(user_data);
|
||||
|
||||
GASSERT(resp == RADIO_RESP_SET_UICC_SUBSCRIPTION);
|
||||
GASSERT(status == RADIO_TX_STATUS_OK);
|
||||
GASSERT(error == RADIO_ERROR_NONE);
|
||||
GASSERT(self->sub_req == req);
|
||||
|
||||
radio_request_unref(self->sub_req);
|
||||
self->sub_req = NULL;
|
||||
DBG("UICC subscription OK for slot %u", self->card.slot);
|
||||
binder_sim_card_subscription_done(self);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_sim_card_subscribe(
|
||||
BinderSimCardObject* self,
|
||||
int app_index)
|
||||
{
|
||||
BinderSimCard* card = &self->card;
|
||||
GBinderWriter args;
|
||||
RadioRequest* req = radio_request_new2(self->g,
|
||||
RADIO_REQ_SET_UICC_SUBSCRIPTION, &args,
|
||||
binder_sim_card_subscribe_cb, NULL, self);
|
||||
RadioSelectUiccSub* sub = gbinder_writer_new0(&args, RadioSelectUiccSub);
|
||||
|
||||
/* setUiccSubscription(serial, SelectUiccSub uiccSub) */
|
||||
DBG("%u,%d", card->slot, app_index);
|
||||
sub->slot = card->slot;
|
||||
sub->appIndex = app_index;
|
||||
sub->actStatus = RADIO_UICC_SUB_ACTIVATE;
|
||||
gbinder_writer_append_buffer_object(&args, sub, sizeof(*sub));
|
||||
|
||||
radio_request_set_retry(req, UICC_SUBSCRIPTION_RETRY_MS, -1);
|
||||
radio_request_set_timeout(req, UICC_SUBSCRIPTION_TIMEOUT_MS);
|
||||
|
||||
/* N.B. Some adaptations never reply to SET_UICC_SUBSCRIPTION request */
|
||||
radio_request_drop(self->sub_req);
|
||||
self->sub_req = req;
|
||||
|
||||
/*
|
||||
* Don't allow any requests other that GET_SIM_STATUS until
|
||||
* we are done with the subscription.
|
||||
*/
|
||||
binder_sim_card_tx_start(self);
|
||||
radio_request_submit(self->sub_req);
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
binder_sim_card_select_app(
|
||||
const BinderSimCardStatus *status)
|
||||
{
|
||||
int i, selected_app = -1;
|
||||
|
||||
for (i = 0; i < status->num_apps; i++) {
|
||||
const int type = status->apps[i].app_type;
|
||||
|
||||
if (type == RADIO_APP_TYPE_USIM || type == RADIO_APP_TYPE_RUIM) {
|
||||
selected_app = i;
|
||||
break;
|
||||
} else if (type != RADIO_APP_TYPE_UNKNOWN && selected_app == -1) {
|
||||
selected_app = i;
|
||||
}
|
||||
}
|
||||
|
||||
DBG("%d", selected_app);
|
||||
return selected_app;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_sim_card_update_app(
|
||||
BinderSimCardObject* self)
|
||||
{
|
||||
BinderSimCard* card = &self->card;
|
||||
const BinderSimCardApp* old_app = card->app;
|
||||
const BinderSimCardStatus* status = card->status;
|
||||
int app_index;
|
||||
|
||||
if (status->card_state == RADIO_CARD_STATE_PRESENT) {
|
||||
if (status->gsm_umts_index >= 0 &&
|
||||
status->gsm_umts_index < status->num_apps) {
|
||||
app_index = status->gsm_umts_index;
|
||||
binder_sim_card_subscription_done(self);
|
||||
} else {
|
||||
app_index = binder_sim_card_select_app(status);
|
||||
if (app_index >= 0 && !self->sub_start_timer) {
|
||||
binder_sim_card_subscribe(self, app_index);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
app_index = -1;
|
||||
binder_sim_card_subscription_done(self);
|
||||
}
|
||||
|
||||
if (app_index >= 0 &&
|
||||
status->apps[app_index].app_type != RADIO_APP_TYPE_UNKNOWN) {
|
||||
card->app = status->apps + app_index;
|
||||
} else {
|
||||
card->app = NULL;
|
||||
}
|
||||
|
||||
if (!binder_sim_card_app_equal(old_app, card->app)) {
|
||||
g_signal_emit(self, binder_sim_card_signals[SIGNAL_APP_CHANGED], 0);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
gboolean
|
||||
binder_sim_card_sub_start_timeout(
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderSimCardObject* self = THIS(user_data);
|
||||
|
||||
DBG("%u", self->card.slot);
|
||||
GASSERT(self->sub_start_timer);
|
||||
self->sub_start_timer = 0;
|
||||
binder_sim_card_update_app(self);
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_sim_card_update_status(
|
||||
BinderSimCardObject* self,
|
||||
BinderSimCardStatus* status)
|
||||
{
|
||||
BinderSimCard* card = &self->card;
|
||||
const int diff = binder_sim_card_status_compare(card->status, status);
|
||||
|
||||
if (diff) {
|
||||
BinderSimCardStatus* old_status = card->status;
|
||||
|
||||
card->status = status;
|
||||
if (diff & BINDER_SIMCARD_STATE_CHANGED &&
|
||||
status->card_state == RADIO_CARD_STATE_PRESENT) {
|
||||
|
||||
/*
|
||||
* SIM card has just appeared, give it some time to
|
||||
* activate the USIM app
|
||||
*/
|
||||
if (self->sub_start_timer) {
|
||||
g_source_remove(self->sub_start_timer);
|
||||
}
|
||||
DBG("started subscription timeout for slot %u", card->slot);
|
||||
self->sub_start_timer = g_timeout_add(UICC_SUBSCRIPTION_START_MS,
|
||||
binder_sim_card_sub_start_timeout, self);
|
||||
}
|
||||
binder_sim_card_update_app(self);
|
||||
|
||||
g_signal_emit(self, binder_sim_card_signals
|
||||
[SIGNAL_STATUS_RECEIVED], 0);
|
||||
|
||||
if (diff & BINDER_SIMCARD_STATUS_CHANGED) {
|
||||
DBG("status changed");
|
||||
g_signal_emit(self, binder_sim_card_signals
|
||||
[SIGNAL_STATUS_CHANGED], 0);
|
||||
}
|
||||
if (diff & BINDER_SIMCARD_STATE_CHANGED) {
|
||||
DBG("state changed");
|
||||
g_signal_emit(self, binder_sim_card_signals
|
||||
[SIGNAL_STATE_CHANGED], 0);
|
||||
}
|
||||
binder_sim_card_status_free(old_status);
|
||||
} else {
|
||||
binder_sim_card_update_app(self);
|
||||
binder_sim_card_status_free(status);
|
||||
g_signal_emit(self, binder_sim_card_signals
|
||||
[SIGNAL_STATUS_RECEIVED], 0);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
BinderSimCardStatus*
|
||||
binder_sim_card_status_new(
|
||||
const RadioCardStatus* radio_status)
|
||||
{
|
||||
const guint num_apps = radio_status->apps.count;
|
||||
BinderSimCardStatus* status = g_malloc0(sizeof(BinderSimCardStatus) +
|
||||
num_apps * sizeof(BinderSimCardApp));
|
||||
|
||||
DBG("card_state=%d, universal_pin_state=%d, gsm_umts_index=%d, "
|
||||
"ims_index=%d, num_apps=%d", radio_status->cardState,
|
||||
radio_status->universalPinState,
|
||||
radio_status->gsmUmtsSubscriptionAppIndex,
|
||||
radio_status->imsSubscriptionAppIndex, num_apps);
|
||||
|
||||
status->card_state = radio_status->cardState;
|
||||
status->pin_state = radio_status->universalPinState;
|
||||
status->gsm_umts_index = radio_status->gsmUmtsSubscriptionAppIndex;
|
||||
status->ims_index = radio_status->imsSubscriptionAppIndex;
|
||||
|
||||
if ((status->num_apps = num_apps) > 0) {
|
||||
const RadioAppStatus* radio_apps = radio_status->apps.data.ptr;
|
||||
guint i;
|
||||
|
||||
status->apps = (BinderSimCardApp*)(status + 1);
|
||||
for (i = 0; i < num_apps; i++) {
|
||||
const RadioAppStatus* radio_app = radio_apps + i;
|
||||
BinderSimCardApp* app = status->apps + i;
|
||||
|
||||
app->app_type = radio_app->appType;
|
||||
app->app_state = radio_app->appState;
|
||||
app->perso_substate = radio_app->persoSubstate;
|
||||
app->pin_replaced = radio_app->pinReplaced;
|
||||
app->pin1_state = radio_app->pin1;
|
||||
app->pin2_state = radio_app->pin2;
|
||||
app->aid = g_strdup(radio_app->aid.data.str);
|
||||
app->label = g_strdup(radio_app->label.data.str);
|
||||
|
||||
DBG("app[%d]: type=%d, state=%d, perso_substate=%d, aid_ptr=%s, "
|
||||
"label=%s, pin1_replaced=%d, pin1=%d, pin2=%d", i,
|
||||
app->app_type, app->app_state, app->perso_substate,
|
||||
app->aid, app->label, app->pin_replaced, app->pin1_state,
|
||||
app->pin2_state);
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_sim_card_status_cb(
|
||||
RadioRequest* req,
|
||||
RADIO_TX_STATUS status,
|
||||
RADIO_RESP resp,
|
||||
RADIO_ERROR error,
|
||||
const GBinderReader* args,
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderSimCardObject* self = THIS(user_data);
|
||||
|
||||
GASSERT(self->status_req);
|
||||
radio_request_unref(self->status_req);
|
||||
self->status_req = NULL;
|
||||
|
||||
if (status == RADIO_TX_STATUS_OK && error == RADIO_ERROR_NONE) {
|
||||
const RadioCardStatus* status_1_0;
|
||||
const RadioCardStatus_1_2* status_1_2;
|
||||
const RadioCardStatus_1_4* status_1_4;
|
||||
BinderSimCardStatus* status = NULL;
|
||||
GBinderReader reader;
|
||||
|
||||
gbinder_reader_copy(&reader, args);
|
||||
switch (resp) {
|
||||
case RADIO_RESP_GET_ICC_CARD_STATUS:
|
||||
status_1_0 = gbinder_reader_read_hidl_struct(&reader,
|
||||
RadioCardStatus);
|
||||
if (status_1_0) {
|
||||
status = binder_sim_card_status_new(status_1_0);
|
||||
}
|
||||
break;
|
||||
case RADIO_RESP_GET_ICC_CARD_STATUS_1_2:
|
||||
status_1_2 = gbinder_reader_read_hidl_struct(&reader,
|
||||
RadioCardStatus_1_2);
|
||||
if (status_1_2) {
|
||||
status = binder_sim_card_status_new(&status_1_2->base);
|
||||
}
|
||||
break;
|
||||
case RADIO_RESP_GET_ICC_CARD_STATUS_RESPONSE_1_4:
|
||||
status_1_4 = gbinder_reader_read_hidl_struct(&reader,
|
||||
RadioCardStatus_1_4);
|
||||
if (status_1_4) {
|
||||
status = binder_sim_card_status_new(&status_1_4->base);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ofono_warn("Unexpected getIccCardStatus response %u", resp);
|
||||
}
|
||||
|
||||
if (status) {
|
||||
binder_sim_card_update_status(self, status);
|
||||
}
|
||||
}
|
||||
binder_sim_card_tx_check(self);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_sim_card_get_status(
|
||||
BinderSimCardObject* self)
|
||||
{
|
||||
if (self->status_req) {
|
||||
/* Retry right away, don't wait for retry timeout to expire */
|
||||
radio_request_retry(self->status_req);
|
||||
} else {
|
||||
self->status_req = radio_request_new2(self->g,
|
||||
RADIO_REQ_GET_ICC_CARD_STATUS, NULL,
|
||||
binder_sim_card_status_cb, NULL, self);
|
||||
|
||||
/*
|
||||
* Start the transaction to not allow any other requests to
|
||||
* interfere with SIM status query.
|
||||
*/
|
||||
binder_sim_card_tx_start(self);
|
||||
radio_request_set_retry(self->status_req, BINDER_RETRY_MS, -1);
|
||||
radio_request_submit(self->status_req);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_sim_card_update_sim_io_active(
|
||||
BinderSimCardObject* self)
|
||||
{
|
||||
/* SIM I/O is considered active for certain period of time after
|
||||
* the last request has completed. That's because SIM_IO requests
|
||||
* are usually submitted in large quantities and quick succession.
|
||||
* Some modems don't like being bothered while they are doing SIM I/O
|
||||
* and some time after that too. That sucks but what else can we
|
||||
* do about it? */
|
||||
BinderSimCard* card = &self->card;
|
||||
const gboolean active = self->sim_io_idle_id ||
|
||||
g_hash_table_size(self->sim_io_pending);
|
||||
|
||||
if (card->sim_io_active != active) {
|
||||
card->sim_io_active = active;
|
||||
DBG("SIM I/O for slot %u is %sactive", card->slot, active ? "" : "in");
|
||||
g_signal_emit(self, binder_sim_card_signals
|
||||
[SIGNAL_SIM_IO_ACTIVE_CHANGED], 0);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
gboolean
|
||||
binder_sim_card_sim_io_idle_cb(
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderSimCardObject* self = THIS(user_data);
|
||||
|
||||
if (++(self->sim_io_idle_count) >= SIM_IO_IDLE_LOOPS) {
|
||||
self->sim_io_idle_id = 0;
|
||||
self->sim_io_idle_count = 0;
|
||||
binder_sim_card_update_sim_io_active(self);
|
||||
return G_SOURCE_REMOVE;
|
||||
} else {
|
||||
return G_SOURCE_CONTINUE;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_sim_card_status_changed(
|
||||
RadioClient* client,
|
||||
RADIO_IND code,
|
||||
const GBinderReader* args,
|
||||
gpointer user_data)
|
||||
{
|
||||
binder_sim_card_get_status(THIS(user_data));
|
||||
}
|
||||
|
||||
/*==========================================================================*
|
||||
* API
|
||||
*==========================================================================*/
|
||||
|
||||
BinderSimCard*
|
||||
binder_sim_card_new(
|
||||
RadioClient* client,
|
||||
guint slot)
|
||||
{
|
||||
BinderSimCardObject* self = g_object_new(THIS_TYPE, NULL);
|
||||
BinderSimCard *card = &self->card;
|
||||
|
||||
DBG("%u", slot);
|
||||
card->slot = slot;
|
||||
self->g = radio_request_group_new(client); /* Keeps ref to client */
|
||||
|
||||
self->event_id[EVENT_SIM_STATUS_CHANGED] =
|
||||
radio_client_add_indication_handler(client,
|
||||
RADIO_IND_SIM_STATUS_CHANGED,
|
||||
binder_sim_card_status_changed, self);
|
||||
self->event_id[EVENT_UICC_SUBSCRIPTION_STATUS_CHANGED] =
|
||||
radio_client_add_indication_handler(client,
|
||||
RADIO_IND_SUBSCRIPTION_STATUS_CHANGED,
|
||||
binder_sim_card_status_changed, self);
|
||||
binder_sim_card_get_status(self);
|
||||
return card;
|
||||
}
|
||||
|
||||
BinderSimCard*
|
||||
binder_sim_card_ref(
|
||||
BinderSimCard* card)
|
||||
{
|
||||
if (G_LIKELY(card)) {
|
||||
g_object_ref(THIS(card));
|
||||
}
|
||||
return card;
|
||||
}
|
||||
|
||||
void
|
||||
binder_sim_card_unref(
|
||||
BinderSimCard* card)
|
||||
{
|
||||
if (G_LIKELY(card)) {
|
||||
g_object_unref(THIS(card));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
binder_sim_card_reset(
|
||||
BinderSimCard* card)
|
||||
{
|
||||
if (G_LIKELY(card)) {
|
||||
BinderSimCardObject* self = binder_sim_card_cast(card);
|
||||
BinderSimCardStatus* status = g_new0(BinderSimCardStatus, 1);
|
||||
|
||||
/* Simulate removal and re-submit the SIM status query */
|
||||
status->card_state = RADIO_CARD_STATE_ABSENT;
|
||||
status->gsm_umts_index = -1;
|
||||
status->ims_index = -1;
|
||||
binder_sim_card_update_status(self, status);
|
||||
binder_sim_card_get_status(self);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
binder_sim_card_request_status(
|
||||
BinderSimCard* card)
|
||||
{
|
||||
if (G_LIKELY(card)) {
|
||||
binder_sim_card_get_status(binder_sim_card_cast(card));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
binder_sim_card_sim_io_started(
|
||||
BinderSimCard* card,
|
||||
gpointer key)
|
||||
{
|
||||
BinderSimCardObject* self = binder_sim_card_cast(card);
|
||||
|
||||
if (G_LIKELY(self) && G_LIKELY(key)) {
|
||||
g_hash_table_insert(self->sim_io_pending, key, key);
|
||||
if (self->sim_io_idle_id) {
|
||||
g_source_remove(self->sim_io_idle_id);
|
||||
self->sim_io_idle_id = 0;
|
||||
self->sim_io_idle_count = 0;
|
||||
}
|
||||
binder_sim_card_update_sim_io_active(self);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
binder_sim_card_sim_io_finished(
|
||||
BinderSimCard* card,
|
||||
gpointer key)
|
||||
{
|
||||
if (G_LIKELY(card) && G_LIKELY(key)) {
|
||||
BinderSimCardObject* self = binder_sim_card_cast(card);
|
||||
|
||||
if (g_hash_table_remove(self->sim_io_pending, key) &&
|
||||
g_hash_table_size(self->sim_io_pending) == 0) {
|
||||
/* Reset the idle loop count */
|
||||
if (self->sim_io_idle_id) {
|
||||
g_source_remove(self->sim_io_idle_id);
|
||||
self->sim_io_idle_count = 0;
|
||||
}
|
||||
self->sim_io_idle_id = g_idle_add(binder_sim_card_sim_io_idle_cb,
|
||||
self);
|
||||
}
|
||||
binder_sim_card_update_sim_io_active(self);
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
binder_sim_card_ready(
|
||||
BinderSimCard* card)
|
||||
{
|
||||
return card && card->app &&
|
||||
((card->app->app_state == RADIO_APP_STATE_READY) ||
|
||||
(card->app->app_state == RADIO_APP_STATE_SUBSCRIPTION_PERSO &&
|
||||
card->app->perso_substate == RADIO_PERSO_SUBSTATE_READY));
|
||||
}
|
||||
|
||||
gulong
|
||||
binder_sim_card_add_status_received_handler(
|
||||
BinderSimCard* card,
|
||||
BinderSimCardFunc fn,
|
||||
void* user_data)
|
||||
{
|
||||
BinderSimCardObject* self = binder_sim_card_cast(card);
|
||||
|
||||
return (G_LIKELY(self) && G_LIKELY(fn)) ? g_signal_connect(self,
|
||||
SIGNAL_STATUS_RECEIVED_NAME, G_CALLBACK(fn), user_data) : 0;
|
||||
}
|
||||
|
||||
gulong
|
||||
binder_sim_card_add_status_changed_handler(
|
||||
BinderSimCard* card,
|
||||
BinderSimCardFunc fn,
|
||||
void* user_data)
|
||||
{
|
||||
BinderSimCardObject* self = binder_sim_card_cast(card);
|
||||
|
||||
return (G_LIKELY(self) && G_LIKELY(fn)) ? g_signal_connect(self,
|
||||
SIGNAL_STATUS_CHANGED_NAME, G_CALLBACK(fn), user_data) : 0;
|
||||
}
|
||||
|
||||
gulong
|
||||
binder_sim_card_add_state_changed_handler(
|
||||
BinderSimCard* card,
|
||||
BinderSimCardFunc fn,
|
||||
void* user_data)
|
||||
{
|
||||
BinderSimCardObject* self = binder_sim_card_cast(card);
|
||||
|
||||
return (G_LIKELY(self) && G_LIKELY(fn)) ? g_signal_connect(self,
|
||||
SIGNAL_STATE_CHANGED_NAME, G_CALLBACK(fn), user_data) : 0;
|
||||
}
|
||||
|
||||
gulong
|
||||
binder_sim_card_add_app_changed_handler(
|
||||
BinderSimCard* card,
|
||||
BinderSimCardFunc fn,
|
||||
void* user_data)
|
||||
{
|
||||
BinderSimCardObject* self = binder_sim_card_cast(card);
|
||||
|
||||
return (G_LIKELY(self) && G_LIKELY(fn)) ? g_signal_connect(self,
|
||||
SIGNAL_APP_CHANGED_NAME, G_CALLBACK(fn), user_data) : 0;
|
||||
}
|
||||
|
||||
gulong
|
||||
binder_sim_card_add_sim_io_active_changed_handler(
|
||||
BinderSimCard* card,
|
||||
BinderSimCardFunc fn,
|
||||
void* user_data)
|
||||
{
|
||||
BinderSimCardObject* self = binder_sim_card_cast(card);
|
||||
|
||||
return (G_LIKELY(self) && G_LIKELY(fn)) ? g_signal_connect(self,
|
||||
SIGNAL_SIM_IO_ACTIVE_CHANGED_NAME, G_CALLBACK(fn), user_data) : 0;
|
||||
}
|
||||
|
||||
void
|
||||
binder_sim_card_remove_handler(
|
||||
BinderSimCard* card,
|
||||
gulong id)
|
||||
{
|
||||
BinderSimCardObject* self = binder_sim_card_cast(card);
|
||||
|
||||
if (G_LIKELY(self) && G_LIKELY(id)) {
|
||||
g_signal_handler_disconnect(self, id);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
binder_sim_card_remove_handlers(
|
||||
BinderSimCard* card,
|
||||
gulong* ids,
|
||||
int n)
|
||||
{
|
||||
gutil_disconnect_handlers(binder_sim_card_cast(card), ids, n);
|
||||
}
|
||||
|
||||
/*==========================================================================*
|
||||
* Internals
|
||||
*==========================================================================*/
|
||||
|
||||
static
|
||||
void
|
||||
binder_sim_card_init(
|
||||
BinderSimCardObject* self)
|
||||
{
|
||||
self->sim_io_pending = g_hash_table_new(g_direct_hash, g_direct_equal);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_sim_card_finalize(
|
||||
GObject* object)
|
||||
{
|
||||
BinderSimCardObject* self = THIS(object);
|
||||
BinderSimCard* card = &self->card;
|
||||
|
||||
if (self->sim_io_idle_id) {
|
||||
g_source_remove(self->sim_io_idle_id);
|
||||
}
|
||||
if (self->sub_start_timer) {
|
||||
g_source_remove(self->sub_start_timer);
|
||||
}
|
||||
g_hash_table_destroy(self->sim_io_pending);
|
||||
|
||||
radio_request_drop(self->status_req);
|
||||
radio_request_drop(self->sub_req);
|
||||
|
||||
radio_client_remove_all_handlers(self->g->client, self->event_id);
|
||||
radio_request_group_unblock(self->g);
|
||||
radio_request_group_cancel(self->g);
|
||||
radio_request_group_unref(self->g);
|
||||
|
||||
binder_sim_card_status_free(card->status);
|
||||
G_OBJECT_CLASS(PARENT_CLASS)->finalize(object);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_sim_card_class_init(
|
||||
BinderSimCardObjectClass* klass)
|
||||
{
|
||||
G_OBJECT_CLASS(klass)->finalize = binder_sim_card_finalize;
|
||||
NEW_SIGNAL_(klass,STATUS_RECEIVED);
|
||||
NEW_SIGNAL(klass,STATUS);
|
||||
NEW_SIGNAL(klass,STATE);
|
||||
NEW_SIGNAL(klass,APP);
|
||||
NEW_SIGNAL(klass,SIM_IO_ACTIVE);
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
168
src/binder_sim_card.h
Normal file
168
src/binder_sim_card.h
Normal file
@@ -0,0 +1,168 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef BINDER_SIM_CARD_H
|
||||
#define BINDER_SIM_CARD_H
|
||||
|
||||
#include "binder_types.h"
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
typedef struct binder_sim_card_app {
|
||||
RADIO_APP_TYPE app_type;
|
||||
RADIO_APP_STATE app_state;
|
||||
RADIO_PERSO_SUBSTATE perso_substate;
|
||||
char* aid;
|
||||
char* label;
|
||||
guint pin_replaced;
|
||||
RADIO_PIN_STATE pin1_state;
|
||||
RADIO_PIN_STATE pin2_state;
|
||||
} BinderSimCardApp;
|
||||
|
||||
typedef struct binder_sim_card_status {
|
||||
RADIO_CARD_STATE card_state;
|
||||
RADIO_PIN_STATE pin_state;
|
||||
int gsm_umts_index;
|
||||
int ims_index;
|
||||
guint num_apps;
|
||||
BinderSimCardApp* apps;
|
||||
} BinderSimCardStatus;
|
||||
|
||||
struct binder_sim_card {
|
||||
GObject object;
|
||||
BinderSimCardStatus* status;
|
||||
const BinderSimCardApp* app;
|
||||
gboolean sim_io_active;
|
||||
guint slot;
|
||||
};
|
||||
|
||||
typedef
|
||||
void
|
||||
(*BinderSimCardFunc)(
|
||||
BinderSimCard* card,
|
||||
void* user_data);
|
||||
|
||||
BinderSimCard*
|
||||
binder_sim_card_new(
|
||||
RadioClient* client,
|
||||
guint slot)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
BinderSimCard*
|
||||
binder_sim_card_ref(
|
||||
BinderSimCard* card)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_sim_card_unref(
|
||||
BinderSimCard* card)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_sim_card_reset(
|
||||
BinderSimCard* card)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_sim_card_request_status(
|
||||
BinderSimCard* card)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_sim_card_sim_io_started(
|
||||
BinderSimCard* card,
|
||||
gpointer key)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_sim_card_sim_io_finished(
|
||||
BinderSimCard* card,
|
||||
gpointer key)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
gboolean
|
||||
binder_sim_card_ready(
|
||||
BinderSimCard* card)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
gulong
|
||||
binder_sim_card_add_status_received_handler(
|
||||
BinderSimCard* card,
|
||||
BinderSimCardFunc fn,
|
||||
void* user_data)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
gulong
|
||||
binder_sim_card_add_status_changed_handler(
|
||||
BinderSimCard* card,
|
||||
BinderSimCardFunc fn,
|
||||
void* user_data)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
gulong
|
||||
binder_sim_card_add_state_changed_handler(
|
||||
BinderSimCard* card,
|
||||
BinderSimCardFunc fn,
|
||||
void* user_data)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
gulong
|
||||
binder_sim_card_add_app_changed_handler(
|
||||
BinderSimCard* card,
|
||||
BinderSimCardFunc fn,
|
||||
void* user_data)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
gulong
|
||||
binder_sim_card_add_sim_io_active_changed_handler(
|
||||
BinderSimCard* card,
|
||||
BinderSimCardFunc fn,
|
||||
void* user_data)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_sim_card_remove_handler(
|
||||
BinderSimCard* card,
|
||||
gulong id)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_sim_card_remove_handlers(
|
||||
BinderSimCard* card,
|
||||
gulong* ids,
|
||||
int n)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
/* Inline wrappers */
|
||||
|
||||
static inline RADIO_APP_TYPE binder_sim_card_app_type(BinderSimCard* card)
|
||||
{ return (card && card->app) ? card->app->app_type :
|
||||
RADIO_APP_TYPE_UNKNOWN; }
|
||||
|
||||
static inline const char* binder_sim_card_app_aid(BinderSimCard* card)
|
||||
{ return (card && card->app) ? card->app->aid : NULL; }
|
||||
|
||||
#define binder_sim_card_remove_all_handlers(net, ids) \
|
||||
binder_sim_card_remove_handlers(net, ids, G_N_ELEMENTS(ids))
|
||||
|
||||
#endif /* BINDER_SIM_CARD_H */
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
214
src/binder_sim_settings.c
Normal file
214
src/binder_sim_settings.c
Normal file
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "binder_base.h"
|
||||
#include "binder_log.h"
|
||||
#include "binder_sim_settings.h"
|
||||
|
||||
#include <ofono/watch.h>
|
||||
|
||||
#include <gutil_misc.h>
|
||||
#include <gutil_macros.h>
|
||||
|
||||
BINDER_BASE_ASSERT_COUNT(BINDER_SIM_SETTINGS_PROPERTY_COUNT);
|
||||
|
||||
enum ofono_watch_events {
|
||||
WATCH_EVENT_IMSI,
|
||||
WATCH_EVENT_COUNT
|
||||
};
|
||||
|
||||
typedef struct binder_sim_settings_object {
|
||||
BinderBase base;
|
||||
BinderSimSettings pub;
|
||||
gulong watch_event_id[WATCH_EVENT_COUNT];
|
||||
struct ofono_watch* watch;
|
||||
char* imsi;
|
||||
} BinderSimSettingsObject;
|
||||
|
||||
typedef BinderBaseClass BinderSimSettingsObjectClass;
|
||||
GType binder_sim_settings_object_get_type() BINDER_INTERNAL;
|
||||
G_DEFINE_TYPE(BinderSimSettingsObject, binder_sim_settings_object,
|
||||
BINDER_TYPE_BASE)
|
||||
#define PARENT_CLASS binder_sim_settings_object_parent_class
|
||||
#define THIS_TYPE binder_sim_settings_object_get_type()
|
||||
#define THIS(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, THIS_TYPE, \
|
||||
BinderSimSettingsObject)
|
||||
|
||||
static inline
|
||||
BinderSimSettingsObject*
|
||||
binder_sim_settings_cast(
|
||||
BinderSimSettings* settings)
|
||||
{
|
||||
return G_LIKELY(settings) ?
|
||||
THIS(G_CAST(settings, BinderSimSettingsObject, pub)) :
|
||||
NULL;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_sim_settings_imsi_changed(
|
||||
struct ofono_watch* watch,
|
||||
void* user_data)
|
||||
{
|
||||
BinderSimSettingsObject* self = THIS(user_data);
|
||||
|
||||
if (g_strcmp0(self->imsi, watch->imsi)) {
|
||||
g_object_ref(self);
|
||||
g_free(self->imsi);
|
||||
self->pub.imsi = self->imsi = g_strdup(watch->imsi);
|
||||
binder_base_emit_property_change(&self->base,
|
||||
BINDER_SIM_SETTINGS_PROPERTY_IMSI);
|
||||
g_object_unref(self);
|
||||
}
|
||||
}
|
||||
|
||||
/*==========================================================================*
|
||||
* API
|
||||
*==========================================================================*/
|
||||
|
||||
BinderSimSettings*
|
||||
binder_sim_settings_new(
|
||||
const char* path,
|
||||
enum ofono_radio_access_mode techs)
|
||||
{
|
||||
BinderSimSettings* settings = NULL;
|
||||
|
||||
if (G_LIKELY(path)) {
|
||||
BinderSimSettingsObject* self = g_object_new(THIS_TYPE, NULL);
|
||||
|
||||
settings = &self->pub;
|
||||
settings->techs = techs;
|
||||
settings->pref = techs;
|
||||
self->watch = ofono_watch_new(path);
|
||||
self->watch_event_id[WATCH_EVENT_IMSI] =
|
||||
ofono_watch_add_imsi_changed_handler(self->watch,
|
||||
binder_sim_settings_imsi_changed, self);
|
||||
settings->imsi = self->imsi = g_strdup(self->watch->imsi);
|
||||
}
|
||||
return settings;
|
||||
}
|
||||
|
||||
BinderSimSettings*
|
||||
binder_sim_settings_ref(
|
||||
BinderSimSettings* settings)
|
||||
{
|
||||
BinderSimSettingsObject* self = binder_sim_settings_cast(settings);
|
||||
|
||||
if (G_LIKELY(self)) {
|
||||
g_object_ref(self);
|
||||
}
|
||||
return settings;
|
||||
}
|
||||
|
||||
void
|
||||
binder_sim_settings_unref(
|
||||
BinderSimSettings* settings)
|
||||
{
|
||||
BinderSimSettingsObject* self = binder_sim_settings_cast(settings);
|
||||
|
||||
if (G_LIKELY(self)) {
|
||||
g_object_unref(self);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
binder_sim_settings_set_pref(
|
||||
BinderSimSettings* settings,
|
||||
enum ofono_radio_access_mode pref)
|
||||
{
|
||||
BinderSimSettingsObject* self = binder_sim_settings_cast(settings);
|
||||
|
||||
if (G_LIKELY(self) && settings->pref != pref) {
|
||||
settings->pref = pref;
|
||||
binder_base_emit_property_change(&self->base,
|
||||
BINDER_SIM_SETTINGS_PROPERTY_PREF);
|
||||
}
|
||||
}
|
||||
|
||||
gulong
|
||||
binder_sim_settings_add_property_handler(
|
||||
BinderSimSettings* settings,
|
||||
BINDER_SIM_SETTINGS_PROPERTY property,
|
||||
BinderSimSettingsPropertyFunc callback,
|
||||
void* user_data)
|
||||
{
|
||||
BinderSimSettingsObject* self = binder_sim_settings_cast(settings);
|
||||
|
||||
return G_LIKELY(self) ? binder_base_add_property_handler(&self->base,
|
||||
property, G_CALLBACK(callback), user_data) : 0;
|
||||
}
|
||||
|
||||
void
|
||||
binder_sim_settings_remove_handler(
|
||||
BinderSimSettings* settings,
|
||||
gulong id)
|
||||
{
|
||||
BinderSimSettingsObject* self = binder_sim_settings_cast(settings);
|
||||
|
||||
if (G_LIKELY(self) && G_LIKELY(id)) {
|
||||
g_signal_handler_disconnect(self, id);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
binder_sim_settings_remove_handlers(
|
||||
BinderSimSettings* settings,
|
||||
gulong* ids,
|
||||
int count)
|
||||
{
|
||||
gutil_disconnect_handlers(binder_sim_settings_cast(settings), ids, count);
|
||||
}
|
||||
|
||||
/*==========================================================================*
|
||||
* Internals
|
||||
*==========================================================================*/
|
||||
|
||||
static
|
||||
void
|
||||
binder_sim_settings_object_init(
|
||||
BinderSimSettingsObject* self)
|
||||
{
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_sim_settings_object_finalize(
|
||||
GObject* object)
|
||||
{
|
||||
BinderSimSettingsObject* self = THIS(object);
|
||||
|
||||
ofono_watch_remove_all_handlers(self->watch, self->watch_event_id);
|
||||
ofono_watch_unref(self->watch);
|
||||
g_free(self->imsi);
|
||||
G_OBJECT_CLASS(PARENT_CLASS)->finalize(object);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_sim_settings_object_class_init(
|
||||
BinderSimSettingsObjectClass* klass)
|
||||
{
|
||||
G_OBJECT_CLASS(klass)->finalize = binder_sim_settings_object_finalize;
|
||||
BINDER_BASE_CLASS(klass)->public_offset =
|
||||
G_STRUCT_OFFSET(BinderSimSettingsObject, pub);
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
97
src/binder_sim_settings.h
Normal file
97
src/binder_sim_settings.h
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef BINDER_SIM_SETTINGS_H
|
||||
#define BINDER_SIM_SETTINGS_H
|
||||
|
||||
#include "binder_types.h"
|
||||
|
||||
#include <ofono/radio-settings.h>
|
||||
|
||||
typedef enum binder_sim_settings_property {
|
||||
BINDER_SIM_SETTINGS_PROPERTY_ANY,
|
||||
BINDER_SIM_SETTINGS_PROPERTY_IMSI,
|
||||
BINDER_SIM_SETTINGS_PROPERTY_PREF,
|
||||
BINDER_SIM_SETTINGS_PROPERTY_COUNT
|
||||
} BINDER_SIM_SETTINGS_PROPERTY;
|
||||
|
||||
struct binder_sim_settings {
|
||||
const char* imsi;
|
||||
enum ofono_radio_access_mode techs; /* Mask */
|
||||
enum ofono_radio_access_mode pref; /* Mask */
|
||||
};
|
||||
|
||||
typedef
|
||||
void
|
||||
(*BinderSimSettingsPropertyFunc)(
|
||||
BinderSimSettings* settings,
|
||||
BINDER_SIM_SETTINGS_PROPERTY property,
|
||||
void* user_data);
|
||||
|
||||
BinderSimSettings*
|
||||
binder_sim_settings_new(
|
||||
const char* path,
|
||||
enum ofono_radio_access_mode techs)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
BinderSimSettings*
|
||||
binder_sim_settings_ref(
|
||||
BinderSimSettings* settings)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_sim_settings_unref(
|
||||
BinderSimSettings* settings)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_sim_settings_set_pref(
|
||||
BinderSimSettings* settings,
|
||||
enum ofono_radio_access_mode pref)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
gulong
|
||||
binder_sim_settings_add_property_handler(
|
||||
BinderSimSettings* settings,
|
||||
BINDER_SIM_SETTINGS_PROPERTY property,
|
||||
BinderSimSettingsPropertyFunc callback,
|
||||
void* user_data)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_sim_settings_remove_handler(
|
||||
BinderSimSettings* settings,
|
||||
gulong id)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_sim_settings_remove_handlers(
|
||||
BinderSimSettings* settings,
|
||||
gulong* ids,
|
||||
int count)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
#define binder_sim_settings_remove_all_handlers(settings,ids) \
|
||||
binder_sim_settings_remove_handlers(settings, ids, G_N_ELEMENTS(ids))
|
||||
|
||||
#endif /* BINDER_SIM_SETTINGS_H */
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
707
src/binder_sms.c
Normal file
707
src/binder_sms.c
Normal file
@@ -0,0 +1,707 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "binder_log.h"
|
||||
#include "binder_modem.h"
|
||||
#include "binder_sms.h"
|
||||
#include "binder_util.h"
|
||||
|
||||
#include <ofono/log.h>
|
||||
#include <ofono/misc.h>
|
||||
#include <ofono/sim.h>
|
||||
#include <ofono/sms.h>
|
||||
#include <ofono/watch.h>
|
||||
|
||||
#include <radio_client.h>
|
||||
#include <radio_request.h>
|
||||
#include <radio_request_group.h>
|
||||
|
||||
#include <gbinder_reader.h>
|
||||
#include <gbinder_writer.h>
|
||||
|
||||
#include <gutil_macros.h>
|
||||
#include <gutil_misc.h>
|
||||
|
||||
#define BINDER_SMS_ACK_RETRY_MS 1000
|
||||
#define BINDER_SMS_ACK_RETRY_COUNT 10
|
||||
|
||||
#define SIM_EFSMS_FILEID 0x6F3C
|
||||
#define EFSMS_LENGTH 176
|
||||
|
||||
static unsigned char sim_path[4] = {0x3F, 0x00, 0x7F, 0x10};
|
||||
|
||||
enum binder_sms_events {
|
||||
SMS_EVENT_NEW_SMS,
|
||||
SMS_EVENT_NEW_STATUS_REPORT,
|
||||
SMS_EVENT_NEW_SMS_ON_SIM,
|
||||
SMS_EVENT_COUNT
|
||||
};
|
||||
|
||||
typedef struct binder_sms {
|
||||
struct ofono_sms* sms;
|
||||
struct ofono_watch* watch;
|
||||
struct ofono_sim_context* sim_context;
|
||||
char* log_prefix;
|
||||
RadioRequestGroup* g;
|
||||
gulong event_id[SMS_EVENT_COUNT];
|
||||
guint register_id;
|
||||
} BinderSms;
|
||||
|
||||
typedef struct binder_sms_cbd {
|
||||
BinderSms* self;
|
||||
union _ofono_sms_cb {
|
||||
ofono_sms_sca_set_cb_t sca_set;
|
||||
ofono_sms_sca_query_cb_t sca_query;
|
||||
ofono_sms_submit_cb_t submit;
|
||||
BinderCallback ptr;
|
||||
} cb;
|
||||
gpointer data;
|
||||
} BinderSmsCbData;
|
||||
|
||||
typedef struct binder_sms_sim_read_data {
|
||||
BinderSms* self;
|
||||
int record;
|
||||
} BinderSmsSimReadData;
|
||||
|
||||
#define DBG_(self,fmt,args...) DBG("%s" fmt, (self)->log_prefix, ##args)
|
||||
|
||||
static inline BinderSms* binder_sms_get_data(struct ofono_sms *sms)
|
||||
{ return ofono_sms_get_data(sms); }
|
||||
|
||||
static
|
||||
BinderSmsCbData*
|
||||
binder_sms_cbd_new(
|
||||
BinderSms* self,
|
||||
BinderCallback cb,
|
||||
void* data)
|
||||
{
|
||||
BinderSmsCbData* cbd = g_slice_new0(BinderSmsCbData);
|
||||
|
||||
cbd->self = self;
|
||||
cbd->cb.ptr = cb;
|
||||
cbd->data = data;
|
||||
return cbd;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_sms_cbd_free(
|
||||
gpointer cbd)
|
||||
{
|
||||
g_slice_free(BinderSmsCbData, cbd);
|
||||
}
|
||||
|
||||
static
|
||||
BinderSmsSimReadData*
|
||||
binder_sms_sim_read_data_new(
|
||||
BinderSms* self,
|
||||
int rec)
|
||||
{
|
||||
BinderSmsSimReadData* rd = g_slice_new0(BinderSmsSimReadData);
|
||||
|
||||
rd->self = self;
|
||||
rd->record = rec;
|
||||
return rd;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_sms_sim_read_data_free(
|
||||
BinderSmsSimReadData* rd)
|
||||
{
|
||||
gutil_slice_free(rd);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_sms_sca_set_cb(
|
||||
RadioRequest* req,
|
||||
RADIO_TX_STATUS status,
|
||||
RADIO_RESP resp,
|
||||
RADIO_ERROR error,
|
||||
const GBinderReader* args,
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderSmsCbData* cbd = user_data;
|
||||
ofono_sms_sca_set_cb_t cb = cbd->cb.sca_set;
|
||||
struct ofono_error err;
|
||||
|
||||
if (status == RADIO_TX_STATUS_OK) {
|
||||
if (resp == RADIO_RESP_SET_SMSC_ADDRESS) {
|
||||
if (error == RADIO_ERROR_NONE) {
|
||||
cb(binder_error_ok(&err), cbd->data);
|
||||
return;
|
||||
} else {
|
||||
ofono_warn("smsc setting error %s",
|
||||
binder_radio_error_string(error));
|
||||
}
|
||||
} else {
|
||||
ofono_error("Unexpected setSmscAddress response %d", resp);
|
||||
}
|
||||
}
|
||||
|
||||
cb(binder_error_failure(&err), cbd->data);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_sms_sca_set(
|
||||
struct ofono_sms* sms,
|
||||
const struct ofono_phone_number* sca,
|
||||
ofono_sms_sca_set_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
BinderSms* self = binder_sms_get_data(sms);
|
||||
char* tmp = NULL;
|
||||
GBinderWriter writer;
|
||||
const char* number = (sca->type == OFONO_NUMBER_TYPE_INTERNATIONAL) ?
|
||||
(tmp = g_strconcat("+", sca->number, NULL)) : sca->number;
|
||||
|
||||
/* setSmscAddress(int32_t serial, string smsc); */
|
||||
RadioRequest* req = radio_request_new2(self->g,
|
||||
RADIO_REQ_SET_SMSC_ADDRESS, &writer, binder_sms_sca_set_cb,
|
||||
binder_sms_cbd_free, binder_sms_cbd_new(self, BINDER_CB(cb), data));
|
||||
|
||||
DBG_(self, "setting sca: %s", number);
|
||||
binder_append_hidl_string(&writer, number);
|
||||
|
||||
if (!radio_request_submit(req)) {
|
||||
struct ofono_error err;
|
||||
|
||||
cb(binder_error_failure(&err), data);
|
||||
}
|
||||
|
||||
radio_request_unref(req);
|
||||
g_free(tmp);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_sms_sca_query_cb(
|
||||
RadioRequest* req,
|
||||
RADIO_TX_STATUS status,
|
||||
RADIO_RESP resp,
|
||||
RADIO_ERROR error,
|
||||
const GBinderReader* args,
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderSmsCbData* cbd = user_data;
|
||||
ofono_sms_sca_query_cb_t cb = cbd->cb.sca_query;
|
||||
struct ofono_error err;
|
||||
|
||||
if (status == RADIO_TX_STATUS_OK) {
|
||||
if (resp == RADIO_RESP_GET_SMSC_ADDRESS) {
|
||||
if (error == RADIO_ERROR_NONE) {
|
||||
GBinderReader reader;
|
||||
const char* smsc;
|
||||
|
||||
gbinder_reader_copy(&reader, args);
|
||||
smsc = gbinder_reader_read_hidl_string_c(&reader);
|
||||
if (smsc) {
|
||||
struct ofono_phone_number sca;
|
||||
|
||||
if (smsc[0] == '+') {
|
||||
smsc++;
|
||||
sca.type = OFONO_NUMBER_TYPE_INTERNATIONAL;
|
||||
} else {
|
||||
sca.type = OFONO_NUMBER_TYPE_UNKNOWN;
|
||||
}
|
||||
g_strlcpy(sca.number, smsc, sizeof(sca.number));
|
||||
DBG("csca_query_cb: %s, %d", sca.number, sca.type);
|
||||
cb(binder_error_ok(&err), &sca, cbd->data);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
ofono_warn("smsc query error %s",
|
||||
binder_radio_error_string(error));
|
||||
}
|
||||
} else {
|
||||
ofono_error("Unexpected getSmscAddress response %d", resp);
|
||||
}
|
||||
}
|
||||
|
||||
/* Error path */
|
||||
cb(binder_error_failure(&err), NULL, cbd->data);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_sms_sca_query(
|
||||
struct ofono_sms* sms,
|
||||
ofono_sms_sca_query_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
BinderSms* self = binder_sms_get_data(sms);
|
||||
RadioRequest* req = radio_request_new2(self->g,
|
||||
RADIO_REQ_GET_SMSC_ADDRESS, NULL, binder_sms_sca_query_cb,
|
||||
binder_sms_cbd_free, binder_sms_cbd_new(self, BINDER_CB(cb), data));
|
||||
|
||||
DBG_(self, "sending csca_query");
|
||||
if (!radio_request_submit(req)) {
|
||||
struct ofono_error err;
|
||||
|
||||
cb(binder_error_failure(&err), NULL, data);
|
||||
}
|
||||
radio_request_unref(req);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_sms_submit_cb(
|
||||
RadioRequest* req,
|
||||
RADIO_TX_STATUS status,
|
||||
RADIO_RESP resp,
|
||||
RADIO_ERROR error,
|
||||
const GBinderReader* args,
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderSmsCbData* cbd = user_data;
|
||||
ofono_sms_submit_cb_t cb = cbd->cb.submit;
|
||||
struct ofono_error err;
|
||||
|
||||
binder_error_init_failure(&err);
|
||||
if (status == RADIO_TX_STATUS_OK) {
|
||||
if (resp == RADIO_RESP_SEND_SMS ||
|
||||
resp == RADIO_RESP_SEND_SMS_EXPECT_MORE) {
|
||||
/*
|
||||
* sendSmsResponse(RadioResponseInfo, SendSmsResult sms);
|
||||
* sendSMSExpectMoreResponse(RadioResponseInfo, SendSmsResult sms);
|
||||
*/
|
||||
if (error == RADIO_ERROR_NONE) {
|
||||
const RadioSendSmsResult* res;
|
||||
GBinderReader reader;
|
||||
|
||||
gbinder_reader_copy(&reader, args);
|
||||
res = gbinder_reader_read_hidl_struct(&reader,
|
||||
RadioSendSmsResult);
|
||||
|
||||
if (res) {
|
||||
DBG("sms msg ref: %d, ack: %s err: %d", res->messageRef,
|
||||
res->ackPDU.data.str, res->errorCode);
|
||||
|
||||
/*
|
||||
* Error is -1 if unknown or not applicable,
|
||||
* otherwise 3GPP 27.005, 3.2.5
|
||||
*/
|
||||
if (res->errorCode > 0) {
|
||||
err.type = OFONO_ERROR_TYPE_CMS;
|
||||
err.error = res->errorCode;
|
||||
} else {
|
||||
/* Success */
|
||||
cb(binder_error_ok(&err), res->messageRef, cbd->data);
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ofono_error("sms send error %s",
|
||||
binder_radio_error_string(error));
|
||||
}
|
||||
} else {
|
||||
ofono_error("Unexpected sendSms response %d", resp);
|
||||
}
|
||||
}
|
||||
/* Error path */
|
||||
cb(&err, 0, cbd->data);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_sms_submit(
|
||||
struct ofono_sms* sms,
|
||||
const unsigned char* pdu,
|
||||
int pdu_len,
|
||||
int tpdu_len,
|
||||
int expect_more,
|
||||
ofono_sms_submit_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
BinderSms* self = binder_sms_get_data(sms);
|
||||
RadioGsmSmsMessage* msg;
|
||||
GBinderWriter writer;
|
||||
char* tpdu;
|
||||
guint parent;
|
||||
int smsc_len;
|
||||
|
||||
/* sendSms(int32 serial, GsmSmsMessage message); */
|
||||
RadioRequest* req = radio_request_new2(self->g, expect_more ?
|
||||
RADIO_REQ_SEND_SMS_EXPECT_MORE : RADIO_REQ_SEND_SMS, &writer,
|
||||
binder_sms_submit_cb, binder_sms_cbd_free,
|
||||
binder_sms_cbd_new(self, BINDER_CB(cb), data));
|
||||
|
||||
DBG("pdu_len: %d, tpdu_len: %d more: %d", pdu_len, tpdu_len, expect_more);
|
||||
|
||||
msg = gbinder_writer_new0(&writer, RadioGsmSmsMessage);
|
||||
|
||||
/*
|
||||
* SMSC address:
|
||||
*
|
||||
* smsc_len == 1, then zero-length SMSC was specified but IRadio
|
||||
* interface expects an empty string for default SMSC.
|
||||
*/
|
||||
smsc_len = pdu_len - tpdu_len;
|
||||
if (smsc_len > 1) {
|
||||
binder_copy_hidl_string_len(&writer, &msg->smscPdu, (char*)
|
||||
pdu, smsc_len);
|
||||
} else {
|
||||
/* Default SMSC address */
|
||||
binder_copy_hidl_string(&writer, &msg->smscPdu, NULL);
|
||||
}
|
||||
|
||||
/* PDU is sent as an ASCII hex string */
|
||||
msg->pdu.len = tpdu_len * 2;
|
||||
tpdu = gbinder_writer_malloc(&writer, msg->pdu.len + 1);
|
||||
ofono_encode_hex(pdu + smsc_len, tpdu_len, tpdu);
|
||||
msg->pdu.data.str = tpdu;
|
||||
DBG_(self, "%s", tpdu);
|
||||
|
||||
/* Write GsmSmsMessage and its strings */
|
||||
parent = gbinder_writer_append_buffer_object(&writer, msg, sizeof(*msg));
|
||||
binder_append_hidl_string_data(&writer, msg, smscPdu, parent);
|
||||
binder_append_hidl_string_data(&writer, msg, pdu, parent);
|
||||
|
||||
/* Submit the request */
|
||||
if (!radio_request_submit(req)) {
|
||||
struct ofono_error err;
|
||||
|
||||
cb(binder_error_failure(&err), 0, data);
|
||||
}
|
||||
radio_request_unref(req);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_ack_delivery_cb(
|
||||
RadioRequest* req,
|
||||
RADIO_TX_STATUS status,
|
||||
RADIO_RESP resp,
|
||||
RADIO_ERROR error,
|
||||
const GBinderReader* args,
|
||||
gpointer user_data)
|
||||
{
|
||||
if (status == RADIO_TX_STATUS_OK) {
|
||||
if (resp == RADIO_RESP_ACKNOWLEDGE_LAST_INCOMING_GSM_SMS) {
|
||||
if (error != RADIO_ERROR_NONE) {
|
||||
ofono_error("SMS acknowledgement failed: %s",
|
||||
binder_radio_error_string(error));
|
||||
}
|
||||
} else {
|
||||
ofono_error("Unexpected acknowledgeLastIncomingGsmSms response %d",
|
||||
resp);
|
||||
}
|
||||
} else {
|
||||
ofono_error("SMS acknowledgement failed");
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_ack_delivery(
|
||||
BinderSms* self,
|
||||
gboolean ok)
|
||||
{
|
||||
GBinderWriter writer;
|
||||
|
||||
/*
|
||||
* acknowledgeLastIncomingGsmSms(int32 serial, bool success,
|
||||
* SmsAcknowledgeFailCause cause);
|
||||
*/
|
||||
RadioRequest* req = radio_request_new2(self->g,
|
||||
RADIO_REQ_ACKNOWLEDGE_LAST_INCOMING_GSM_SMS, &writer,
|
||||
binder_ack_delivery_cb, NULL, NULL);
|
||||
|
||||
DBG_(self, "%s", ok ? "ok" : "fail");
|
||||
gbinder_writer_append_bool(&writer, ok);
|
||||
gbinder_writer_append_int32(&writer, ok ? RADIO_SMS_ACK_FAIL_NONE :
|
||||
RADIO_SMS_ACK_FAIL_UNSPECIFIED_ERROR);
|
||||
|
||||
radio_request_set_retry(req, BINDER_SMS_ACK_RETRY_MS,
|
||||
BINDER_SMS_ACK_RETRY_COUNT);
|
||||
radio_request_submit(req);
|
||||
radio_request_unref(req);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_sms_notify(
|
||||
RadioClient* client,
|
||||
RADIO_IND code,
|
||||
const GBinderReader* args,
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderSms* self = user_data;
|
||||
GBinderReader reader;
|
||||
const guint8* pdu;
|
||||
gsize len;
|
||||
|
||||
/*
|
||||
* newSms(RadioIndicationType, vec<uint8_t> pdu);
|
||||
* newSmsStatusReport(RadioIndicationType, vec<uint8_t> pdu);
|
||||
*/
|
||||
gbinder_reader_copy(&reader, args);
|
||||
pdu = gbinder_reader_read_hidl_byte_vec(&reader, &len);
|
||||
if (pdu) {
|
||||
const guint pdu_len = (guint) len;
|
||||
const guint smsc_len = (guint) pdu[0] + 1;
|
||||
|
||||
ofono_info("%s, %u bytes",(code == RADIO_IND_NEW_SMS) ?
|
||||
"incoming sms" : "sms status", pdu_len);
|
||||
if (pdu_len > smsc_len) {
|
||||
/* The PDU starts with the SMSC address per TS 27.005 (+CMT:) */
|
||||
const guint tpdu_len = pdu_len - smsc_len;
|
||||
|
||||
DBG_(self, "smsc: %s", binder_print_hex(pdu, smsc_len));
|
||||
DBG_(self, "tpdu: %s", binder_print_hex(pdu + smsc_len, tpdu_len));
|
||||
|
||||
switch (code) {
|
||||
case RADIO_IND_NEW_SMS:
|
||||
ofono_sms_deliver_notify(self->sms, pdu, pdu_len, tpdu_len);
|
||||
binder_ack_delivery(self, TRUE);
|
||||
break;
|
||||
case RADIO_IND_NEW_SMS_STATUS_REPORT:
|
||||
ofono_sms_status_notify(self->sms, pdu, pdu_len, tpdu_len);
|
||||
binder_ack_delivery(self, TRUE);
|
||||
break;
|
||||
default:
|
||||
binder_ack_delivery(self, FALSE);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
ofono_error("Unable to parse SMS notification");
|
||||
binder_ack_delivery(self, FALSE);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_sms_delete_on_sim_cb(
|
||||
RadioRequest* req,
|
||||
RADIO_TX_STATUS status,
|
||||
RADIO_RESP resp,
|
||||
RADIO_ERROR error,
|
||||
const GBinderReader* args,
|
||||
gpointer user_data)
|
||||
{
|
||||
if (status == RADIO_TX_STATUS_OK) {
|
||||
if (resp == RADIO_RESP_DELETE_SMS_ON_SIM) {
|
||||
if (error == RADIO_ERROR_NONE) {
|
||||
ofono_info("sms deleted from sim");
|
||||
} else {
|
||||
ofono_warn("Failed to delete sms from sim: %s",
|
||||
binder_radio_error_string(error));
|
||||
}
|
||||
} else {
|
||||
ofono_error("Unexpected deleteSmsOnSim response %d", resp);
|
||||
}
|
||||
} else {
|
||||
ofono_error("Deleting SMS from SIM failed");
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_sms_on_sim_cb(
|
||||
int ok,
|
||||
int total_length,
|
||||
int record,
|
||||
const unsigned char* sdata,
|
||||
int length,
|
||||
void* userdata)
|
||||
{
|
||||
BinderSmsSimReadData* cbd = userdata;
|
||||
BinderSms* self = cbd->self;
|
||||
|
||||
/*
|
||||
* EFsms contains status byte followed by SMS PDU (including
|
||||
* SMSC address) per TS 31.103 4.2.12
|
||||
*/
|
||||
if (ok) {
|
||||
if (length > 1) {
|
||||
/* Skip status byte */
|
||||
guint pdu_len = length - 1;
|
||||
const guint8* pdu = sdata + 1;
|
||||
const guint smsc_len = (guint) pdu[0] + 1;
|
||||
|
||||
if (pdu_len > smsc_len) {
|
||||
RadioRequest* req;
|
||||
GBinderWriter writer;
|
||||
const guint tpdu_len = pdu_len - smsc_len;
|
||||
|
||||
ofono_info("read sms from sim, %u bytes", pdu_len);
|
||||
DBG_(self, "smsc: %s", binder_print_hex(pdu, smsc_len));
|
||||
DBG_(self, "tpdu: %s", binder_print_hex(pdu + smsc_len,
|
||||
tpdu_len));
|
||||
|
||||
ofono_sms_deliver_notify(self->sms, pdu, pdu_len, tpdu_len);
|
||||
|
||||
/* deleteSmsOnSim(int32 serial, int32 index); */
|
||||
DBG_(self, "deleting record: %d", cbd->record);
|
||||
req = radio_request_new2(self->g,
|
||||
RADIO_REQ_DELETE_SMS_ON_SIM, &writer,
|
||||
binder_sms_delete_on_sim_cb, NULL, NULL);
|
||||
gbinder_writer_append_int32(&writer, cbd->record);
|
||||
radio_request_submit(req);
|
||||
radio_request_unref(req);
|
||||
} else {
|
||||
ofono_warn("Failed to extract PDU from EFsms");
|
||||
}
|
||||
} else {
|
||||
ofono_warn("Empty EFsms?");
|
||||
}
|
||||
} else {
|
||||
ofono_error("Cannot read SMS from SIM");
|
||||
}
|
||||
|
||||
binder_sms_sim_read_data_free(cbd);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_sms_on_sim(
|
||||
RadioClient* client,
|
||||
RADIO_IND code,
|
||||
const GBinderReader* args,
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderSms* self = user_data;
|
||||
GBinderReader reader;
|
||||
gint32 rec;
|
||||
|
||||
ofono_info("new sms on sim");
|
||||
|
||||
/* newSmsOnSim(RadioIndicationType type, int32 recordNumber); */
|
||||
GASSERT(code == RADIO_IND_NEW_SMS_ON_SIM);
|
||||
gbinder_reader_copy(&reader, args);
|
||||
if (gbinder_reader_read_int32(&reader, &rec)) {
|
||||
DBG("rec %d", rec);
|
||||
if (self->sim_context) {
|
||||
ofono_sim_read_record(self->sim_context, SIM_EFSMS_FILEID,
|
||||
OFONO_SIM_FILE_STRUCTURE_FIXED, rec, EFSMS_LENGTH,
|
||||
sim_path, sizeof(sim_path), binder_sms_on_sim_cb,
|
||||
binder_sms_sim_read_data_new(self, rec));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
gboolean binder_sms_register(
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderSms* self = user_data;
|
||||
RadioClient* client = self->g->client;
|
||||
|
||||
DBG("");
|
||||
GASSERT(self->register_id);
|
||||
self->register_id = 0;
|
||||
|
||||
ofono_sms_register(self->sms);
|
||||
|
||||
/* Register event handlers */
|
||||
self->event_id[SMS_EVENT_NEW_SMS] =
|
||||
radio_client_add_indication_handler(client,
|
||||
RADIO_IND_NEW_SMS, binder_sms_notify, self);
|
||||
self->event_id[SMS_EVENT_NEW_STATUS_REPORT] =
|
||||
radio_client_add_indication_handler(client,
|
||||
RADIO_IND_NEW_SMS_STATUS_REPORT, binder_sms_notify, self);
|
||||
self->event_id[SMS_EVENT_NEW_SMS_ON_SIM] =
|
||||
radio_client_add_indication_handler(client,
|
||||
RADIO_IND_NEW_SMS_ON_SIM, binder_sms_on_sim, self);
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
binder_sms_probe(
|
||||
struct ofono_sms* sms,
|
||||
unsigned int vendor,
|
||||
void* data)
|
||||
{
|
||||
BinderModem* modem = binder_modem_get_data(data);
|
||||
BinderSms* self = g_new0(BinderSms, 1);
|
||||
|
||||
self->log_prefix = binder_dup_prefix(modem->log_prefix);
|
||||
DBG_(self, "");
|
||||
|
||||
self->watch = ofono_watch_new(binder_modem_get_path(modem));
|
||||
self->sim_context = ofono_sim_context_create(self->watch->sim);
|
||||
self->g = radio_request_group_new(modem->client); /* Keeps ref to client */
|
||||
self->sms = sms;
|
||||
|
||||
GASSERT(self->sim_context);
|
||||
self->register_id = g_idle_add(binder_sms_register, self);
|
||||
ofono_sms_set_data(sms, self);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_sms_remove(
|
||||
struct ofono_sms* sms)
|
||||
{
|
||||
BinderSms* self = binder_sms_get_data(sms);
|
||||
|
||||
DBG_(self, "");
|
||||
|
||||
if (self->sim_context) {
|
||||
ofono_sim_context_free(self->sim_context);
|
||||
}
|
||||
|
||||
if (self->register_id) {
|
||||
g_source_remove(self->register_id);
|
||||
}
|
||||
|
||||
radio_client_remove_all_handlers(self->g->client, self->event_id);
|
||||
radio_request_group_cancel(self->g);
|
||||
radio_request_group_unref(self->g);
|
||||
|
||||
g_free(self->log_prefix);
|
||||
g_free(self);
|
||||
|
||||
ofono_sms_set_data(sms, NULL);
|
||||
}
|
||||
|
||||
/*==========================================================================*
|
||||
* API
|
||||
*==========================================================================*/
|
||||
|
||||
static const struct ofono_sms_driver binder_sms_driver = {
|
||||
.name = BINDER_DRIVER,
|
||||
.probe = binder_sms_probe,
|
||||
.remove = binder_sms_remove,
|
||||
.sca_query = binder_sms_sca_query,
|
||||
.sca_set = binder_sms_sca_set,
|
||||
.submit = binder_sms_submit
|
||||
};
|
||||
|
||||
void
|
||||
binder_sms_init()
|
||||
{
|
||||
ofono_sms_driver_register(&binder_sms_driver);
|
||||
}
|
||||
|
||||
void
|
||||
binder_sms_cleanup()
|
||||
{
|
||||
ofono_sms_driver_unregister(&binder_sms_driver);
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
37
src/binder_sms.h
Normal file
37
src/binder_sms.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef BINDER_SMS_H
|
||||
#define BINDER_SMS_H
|
||||
|
||||
#include "binder_types.h"
|
||||
|
||||
void
|
||||
binder_sms_init(void)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_sms_cleanup(void)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
#endif /* BINDER_SMS_H */
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
425
src/binder_stk.c
Normal file
425
src/binder_stk.c
Normal file
@@ -0,0 +1,425 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "binder_log.h"
|
||||
#include "binder_modem.h"
|
||||
#include "binder_stk.h"
|
||||
#include "binder_util.h"
|
||||
|
||||
#include <ofono/log.h>
|
||||
#include <ofono/stk.h>
|
||||
|
||||
#include <radio_client.h>
|
||||
#include <radio_request.h>
|
||||
#include <radio_request_group.h>
|
||||
|
||||
#include <gbinder_reader.h>
|
||||
#include <gbinder_writer.h>
|
||||
|
||||
enum binder_stk_events {
|
||||
STK_EVENT_PROACTIVE_COMMAND,
|
||||
STK_EVENT_SESSION_END,
|
||||
STK_EVENT_NOTIFY,
|
||||
STK_EVENT_COUNT
|
||||
};
|
||||
|
||||
typedef struct binder_stk {
|
||||
struct ofono_stk* stk;
|
||||
char* log_prefix;
|
||||
RadioRequestGroup* g;
|
||||
gulong event_id[STK_EVENT_COUNT];
|
||||
guint register_id;
|
||||
} BinderStk;
|
||||
|
||||
typedef struct binder_stk_cbd {
|
||||
BinderStk* self;
|
||||
union _ofono_stk_cb {
|
||||
ofono_stk_envelope_cb_t envelope;
|
||||
ofono_stk_generic_cb_t generic;
|
||||
BinderCallback ptr;
|
||||
} cb;
|
||||
gpointer data;
|
||||
} BinderStkCbData;
|
||||
|
||||
#define DBG_(cd,fmt,args...) DBG("%s" fmt, (cd)->log_prefix, ##args)
|
||||
|
||||
static inline BinderStk* binder_stk_get_data(struct ofono_stk* stk)
|
||||
{ return ofono_stk_get_data(stk); }
|
||||
|
||||
static
|
||||
BinderStkCbData*
|
||||
binder_stk_cbd_new(
|
||||
BinderStk* self,
|
||||
BinderCallback cb,
|
||||
void* data)
|
||||
{
|
||||
BinderStkCbData* cbd = g_slice_new0(BinderStkCbData);
|
||||
|
||||
cbd->self = self;
|
||||
cbd->cb.ptr = cb;
|
||||
cbd->data = data;
|
||||
return cbd;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_stk_cbd_free(
|
||||
gpointer cbd)
|
||||
{
|
||||
g_slice_free(BinderStkCbData, cbd);
|
||||
}
|
||||
|
||||
static void binder_stk_envelope_cb(
|
||||
RadioRequest* req,
|
||||
RADIO_TX_STATUS status,
|
||||
RADIO_RESP resp,
|
||||
RADIO_ERROR error,
|
||||
const GBinderReader* args,
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderStkCbData* cbd = user_data;
|
||||
ofono_stk_envelope_cb_t cb = cbd->cb.envelope;
|
||||
struct ofono_error err;
|
||||
|
||||
DBG_(cbd->self, "");
|
||||
if (status == RADIO_TX_STATUS_OK) {
|
||||
if (resp == RADIO_RESP_SEND_ENVELOPE) {
|
||||
if (error == RADIO_ERROR_NONE) {
|
||||
cb(binder_error_ok(&err), NULL, 0, cbd->data);
|
||||
return;
|
||||
} else {
|
||||
ofono_warn("Error sending envelope: %s",
|
||||
binder_radio_error_string(error));
|
||||
}
|
||||
} else {
|
||||
ofono_error("Unexpected sendEnvelope response %d", resp);
|
||||
}
|
||||
}
|
||||
cb(binder_error_failure(&err), NULL, 0, cbd->data);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_stk_envelope(
|
||||
struct ofono_stk* stk,
|
||||
int length,
|
||||
const unsigned char* cmd,
|
||||
ofono_stk_envelope_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
BinderStk* self = binder_stk_get_data(stk);
|
||||
char* hex = binder_encode_hex(cmd, length);
|
||||
GBinderWriter writer;
|
||||
|
||||
/* sendEnvelope(int32 serial, string command); */
|
||||
RadioRequest* req = radio_request_new2(self->g,
|
||||
RADIO_REQ_SEND_ENVELOPE, &writer,
|
||||
binder_stk_envelope_cb, binder_stk_cbd_free,
|
||||
binder_stk_cbd_new(self, BINDER_CB(cb), data));
|
||||
|
||||
DBG("envelope %s", hex);
|
||||
gbinder_writer_add_cleanup(&writer, g_free, hex);
|
||||
gbinder_writer_append_hidl_string(&writer, hex);
|
||||
radio_request_submit(req);
|
||||
radio_request_unref(req);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_stk_terminal_response_cb(
|
||||
RadioRequest* req,
|
||||
RADIO_TX_STATUS status,
|
||||
RADIO_RESP resp,
|
||||
RADIO_ERROR error,
|
||||
const GBinderReader* args,
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderStkCbData* cbd = user_data;
|
||||
ofono_stk_generic_cb_t cb = cbd->cb.generic;
|
||||
struct ofono_error err;
|
||||
|
||||
DBG_(cbd->self, "");
|
||||
if (status == RADIO_TX_STATUS_OK) {
|
||||
if (resp == RADIO_RESP_SEND_TERMINAL_RESPONSE_TO_SIM) {
|
||||
if (error == RADIO_ERROR_NONE) {
|
||||
cb(binder_error_ok(&err), cbd->data);
|
||||
return;
|
||||
} else {
|
||||
ofono_warn("Error sending terminal response: %s",
|
||||
binder_radio_error_string(error));
|
||||
}
|
||||
} else {
|
||||
ofono_error("Unexpected sendTerminalResponseToSim response %d",
|
||||
resp);
|
||||
}
|
||||
}
|
||||
cb(binder_error_failure(&err), cbd->data);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_stk_terminal_response(
|
||||
struct ofono_stk* stk,
|
||||
int length,
|
||||
const unsigned char* resp,
|
||||
ofono_stk_generic_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
BinderStk* self = binder_stk_get_data(stk);
|
||||
char* hex = binder_encode_hex(resp, length);
|
||||
GBinderWriter writer;
|
||||
|
||||
/* sendTerminalResponseToSim(int32 serial, string commandResponse); */
|
||||
RadioRequest* req = radio_request_new2(self->g,
|
||||
RADIO_REQ_SEND_TERMINAL_RESPONSE_TO_SIM, &writer,
|
||||
binder_stk_terminal_response_cb, binder_stk_cbd_free,
|
||||
binder_stk_cbd_new(self, BINDER_CB(cb), data));
|
||||
|
||||
DBG_(self, "terminal response: %s", hex);
|
||||
gbinder_writer_add_cleanup(&writer, g_free, hex);
|
||||
gbinder_writer_append_hidl_string(&writer, hex);
|
||||
radio_request_submit(req);
|
||||
radio_request_unref(req);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_stk_user_confirmation(
|
||||
struct ofono_stk* stk,
|
||||
ofono_bool_t confirm)
|
||||
{
|
||||
BinderStk* self = binder_stk_get_data(stk);
|
||||
GBinderWriter writer;
|
||||
|
||||
/* handleStkCallSetupRequestFromSim(int32 serial, bool accept); */
|
||||
RadioRequest* req = radio_request_new2(self->g,
|
||||
RADIO_REQ_HANDLE_STK_CALL_SETUP_REQUEST_FROM_SIM, &writer,
|
||||
NULL, NULL, NULL);
|
||||
|
||||
DBG_(self, "%d", confirm);
|
||||
gbinder_writer_append_bool(&writer, confirm);
|
||||
radio_request_submit(req);
|
||||
radio_request_unref(req);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_stk_proactive_command(
|
||||
RadioClient* client,
|
||||
RADIO_IND code,
|
||||
const GBinderReader* args,
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderStk* self = user_data;
|
||||
GBinderReader reader;
|
||||
const char* pcmd;
|
||||
void* pdu;
|
||||
guint len;
|
||||
|
||||
/*
|
||||
* stkProactiveCommand(RadioIndicationType, string cmd);
|
||||
*
|
||||
* cmd - SAT/USAT proactive represented as byte array starting with
|
||||
* command tag.
|
||||
*
|
||||
* Refer to ETSI TS 102.223 section 9.4 for command types.
|
||||
*/
|
||||
gbinder_reader_copy(&reader, args);
|
||||
pcmd = gbinder_reader_read_hidl_string_c(&reader);
|
||||
pdu = binder_decode_hex(pcmd, -1, &len);
|
||||
if (pdu) {
|
||||
DBG_(self, "pcmd: %s", pcmd);
|
||||
ofono_stk_proactive_command_notify(self->stk, len, pdu);
|
||||
g_free(pdu);
|
||||
} else {
|
||||
ofono_warn("Failed to parse STK command %s", pcmd);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_stk_event_notify(
|
||||
RadioClient* client,
|
||||
RADIO_IND code,
|
||||
const GBinderReader* args,
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderStk* self = user_data;
|
||||
GBinderReader reader;
|
||||
const char* pcmd;
|
||||
void* pdu;
|
||||
guint len;
|
||||
|
||||
/*
|
||||
* stkEventNotify(RadioIndicationType, string cmd);
|
||||
*
|
||||
* cmd - SAT/USAT commands or responses sent by ME to SIM or commands
|
||||
* handled by ME, represented as byte array starting with first byte
|
||||
* of response data for command tag.
|
||||
*
|
||||
* Refer to ETSI TS 102.223 section 9.4 for command types.
|
||||
*/
|
||||
gbinder_reader_copy(&reader, args);
|
||||
pcmd = gbinder_reader_read_hidl_string_c(&reader);
|
||||
pdu = binder_decode_hex(pcmd, -1, &len);
|
||||
if (pdu) {
|
||||
DBG_(self, "pcmd: %s", pcmd);
|
||||
ofono_stk_proactive_command_handled_notify(self->stk, len, pdu);
|
||||
g_free(pdu);
|
||||
} else {
|
||||
ofono_warn("Failed to parse STK event %s", pcmd);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_stk_session_end_notify(
|
||||
RadioClient* client,
|
||||
RADIO_IND code,
|
||||
const GBinderReader* args,
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderStk* self = user_data;
|
||||
|
||||
DBG_(self, "");
|
||||
/* stkSessionEnd(RadioIndicationType); */
|
||||
ofono_stk_proactive_session_end_notify(self->stk);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_stk_agent_ready(
|
||||
struct ofono_stk* stk)
|
||||
{
|
||||
BinderStk* self = binder_stk_get_data(stk);
|
||||
RadioClient* client = self->g->client;
|
||||
|
||||
DBG_(self, "");
|
||||
|
||||
if (!self->event_id[STK_EVENT_PROACTIVE_COMMAND]) {
|
||||
DBG_(self, "Subscribing for notifications");
|
||||
self->event_id[STK_EVENT_PROACTIVE_COMMAND] =
|
||||
radio_client_add_indication_handler(client,
|
||||
RADIO_IND_STK_PROACTIVE_COMMAND,
|
||||
binder_stk_proactive_command, self);
|
||||
|
||||
GASSERT(!self->event_id[STK_EVENT_SESSION_END]);
|
||||
self->event_id[STK_EVENT_SESSION_END] =
|
||||
radio_client_add_indication_handler(client,
|
||||
RADIO_IND_STK_SESSION_END,
|
||||
binder_stk_session_end_notify, self);
|
||||
|
||||
GASSERT(!self->event_id[STK_EVENT_NOTIFY]);
|
||||
self->event_id[STK_EVENT_NOTIFY] =
|
||||
radio_client_add_indication_handler(client,
|
||||
RADIO_IND_STK_EVENT_NOTIFY,
|
||||
binder_stk_event_notify, self);
|
||||
|
||||
/* reportStkServiceIsRunning(int32 serial); */
|
||||
binder_submit_request(self->g, RADIO_REQ_REPORT_STK_SERVICE_IS_RUNNING);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
gboolean binder_stk_register(
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderStk* self = user_data;
|
||||
|
||||
DBG("");
|
||||
GASSERT(self->register_id);
|
||||
self->register_id = 0;
|
||||
|
||||
ofono_stk_register(self->stk);
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
binder_stk_probe(
|
||||
struct ofono_stk* stk,
|
||||
unsigned int vendor,
|
||||
void* data)
|
||||
{
|
||||
BinderModem* modem = binder_modem_get_data(data);
|
||||
BinderStk* self = g_new0(BinderStk, 1);
|
||||
|
||||
self->stk = stk;
|
||||
self->g = radio_request_group_new(modem->client); /* Keeps ref to client */
|
||||
self->log_prefix = binder_dup_prefix(modem->log_prefix);
|
||||
self->register_id = g_idle_add(binder_stk_register, self);
|
||||
|
||||
DBG_(self, "");
|
||||
ofono_stk_set_data(stk, self);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_stk_remove(
|
||||
struct ofono_stk* stk)
|
||||
{
|
||||
BinderStk* self = binder_stk_get_data(stk);
|
||||
|
||||
DBG_(self, "");
|
||||
|
||||
if (self->register_id) {
|
||||
g_source_remove(self->register_id);
|
||||
}
|
||||
|
||||
radio_client_remove_all_handlers(self->g->client, self->event_id);
|
||||
radio_request_group_cancel(self->g);
|
||||
radio_request_group_unref(self->g);
|
||||
|
||||
g_free(self->log_prefix);
|
||||
g_free(self);
|
||||
|
||||
ofono_stk_set_data(stk, NULL);
|
||||
}
|
||||
|
||||
/*==========================================================================*
|
||||
* API
|
||||
*==========================================================================*/
|
||||
|
||||
static const struct ofono_stk_driver binder_stk_driver = {
|
||||
.name = BINDER_DRIVER,
|
||||
.probe = binder_stk_probe,
|
||||
.remove = binder_stk_remove,
|
||||
.envelope = binder_stk_envelope,
|
||||
.terminal_response = binder_stk_terminal_response,
|
||||
.user_confirmation = binder_stk_user_confirmation,
|
||||
.ready = binder_stk_agent_ready
|
||||
};
|
||||
|
||||
void
|
||||
binder_stk_init()
|
||||
{
|
||||
ofono_stk_driver_register(&binder_stk_driver);
|
||||
}
|
||||
|
||||
void
|
||||
binder_stk_cleanup()
|
||||
{
|
||||
ofono_stk_driver_unregister(&binder_stk_driver);
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
37
src/binder_stk.h
Normal file
37
src/binder_stk.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef BINDER_STK_H
|
||||
#define BINDER_STK_H
|
||||
|
||||
#include "binder_types.h"
|
||||
|
||||
void
|
||||
binder_stk_init(void)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_stk_cleanup(void)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
#endif /* BINDER_STK_H */
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
107
src/binder_types.h
Normal file
107
src/binder_types.h
Normal file
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef BINDER_TYPES_H
|
||||
#define BINDER_TYPES_H
|
||||
|
||||
#include <radio_types.h>
|
||||
#include <radio_config_types.h>
|
||||
#include <ofono/radio-settings.h>
|
||||
|
||||
typedef struct binder_data BinderData;
|
||||
typedef struct binder_data_manager BinderDataManager;
|
||||
typedef struct binder_devmon BinderDevmon;
|
||||
typedef struct binder_logger BinderLogger;
|
||||
typedef struct binder_modem BinderModem;
|
||||
typedef struct binder_network BinderNetwork;
|
||||
typedef struct binder_radio_caps BinderRadioCaps;
|
||||
typedef struct binder_radio_caps_manager BinderRadioCapsManager;
|
||||
typedef struct binder_radio_caps_request BinderRadioCapsRequest;
|
||||
typedef struct binder_radio BinderRadio;
|
||||
typedef struct binder_sim_card BinderSimCard;
|
||||
typedef struct binder_sim_settings BinderSimSettings;
|
||||
|
||||
typedef enum binder_feature_mask {
|
||||
BINDER_FEATURE_NONE = 0,
|
||||
BINDER_FEATURE_CBS = 0x0001, /* cbs */
|
||||
BINDER_FEATURE_DATA = 0x0002, /* data */
|
||||
BINDER_FEATURE_NETREG = 0x0004, /* netreg */
|
||||
BINDER_FEATURE_PHONEBOOK = 0x0008, /* pb */
|
||||
BINDER_FEATURE_RADIO_SETTINGS = 0x0010, /* rat */
|
||||
BINDER_FEATURE_SIM_AUTH = 0x0020, /* auth */
|
||||
BINDER_FEATURE_SMS = 0x0040, /* sms */
|
||||
BINDER_FEATURE_STK = 0x0080, /* stk */
|
||||
BINDER_FEATURE_USSD = 0x0100, /* ussd */
|
||||
BINDER_FEATURE_VOICE = 0x0200, /* voice */
|
||||
BINDER_FEATURE_ALL = 0x03ff /* all */
|
||||
} BINDER_FEATURE_MASK;
|
||||
|
||||
typedef struct binder_slot_config {
|
||||
guint slot;
|
||||
int cell_info_interval_short_ms;
|
||||
int cell_info_interval_long_ms;
|
||||
int network_mode_timeout_ms;
|
||||
int network_selection_timeout_ms;
|
||||
int signal_strength_dbm_weak;
|
||||
int signal_strength_dbm_strong;
|
||||
enum ofono_radio_access_mode techs;
|
||||
RADIO_PREF_NET_TYPE lte_network_mode;
|
||||
RADIO_PREF_NET_TYPE umts_network_mode;
|
||||
BINDER_FEATURE_MASK features;
|
||||
gboolean query_available_band_mode;
|
||||
gboolean empty_pin_query;
|
||||
gboolean radio_power_cycle;
|
||||
gboolean confirm_radio_power_on;
|
||||
gboolean replace_strange_oper;
|
||||
gboolean force_gsm_when_radio_off;
|
||||
gboolean use_data_profiles;
|
||||
RADIO_DATA_PROFILE_ID mms_data_profile_id;
|
||||
GUtilInts* local_hangup_reasons;
|
||||
GUtilInts* remote_hangup_reasons;
|
||||
} BinderSlotConfig;
|
||||
|
||||
#define BINDER_DRIVER "binder"
|
||||
#define BINDER_INTERNAL G_GNUC_INTERNAL
|
||||
|
||||
#define BINDER_RETRY_SECS (2)
|
||||
#define BINDER_RETRY_MS (BINDER_RETRY_SECS * 1000)
|
||||
|
||||
typedef void (*BinderCallback)(void);
|
||||
#define BINDER_CB(f) ((BinderCallback)(f))
|
||||
|
||||
#define OFONO_RADIO_ACCESS_MODE_ALL (\
|
||||
OFONO_RADIO_ACCESS_MODE_GSM |\
|
||||
OFONO_RADIO_ACCESS_MODE_UMTS |\
|
||||
OFONO_RADIO_ACCESS_MODE_LTE)
|
||||
|
||||
#define OFONO_RADIO_ACCESS_MODE_NONE \
|
||||
((enum ofono_radio_access_mode) 0)
|
||||
|
||||
/* Masks */
|
||||
#define OFONO_RADIO_ACCESS_GSM_MASK OFONO_RADIO_ACCESS_MODE_GSM
|
||||
#define OFONO_RADIO_ACCESS_UMTS_MASK \
|
||||
(OFONO_RADIO_ACCESS_MODE_UMTS | (OFONO_RADIO_ACCESS_MODE_UMTS - 1))
|
||||
#define OFONO_RADIO_ACCESS_LTE_MASK \
|
||||
(OFONO_RADIO_ACCESS_MODE_LTE | (OFONO_RADIO_ACCESS_MODE_LTE - 1))
|
||||
|
||||
#endif /* BINDER_TYPES_H */
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
360
src/binder_ussd.c
Normal file
360
src/binder_ussd.c
Normal file
@@ -0,0 +1,360 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "binder_log.h"
|
||||
#include "binder_modem.h"
|
||||
#include "binder_ussd.h"
|
||||
#include "binder_util.h"
|
||||
|
||||
#include <ofono/ussd.h>
|
||||
#include <ofono/misc.h>
|
||||
|
||||
#include <radio_client.h>
|
||||
#include <radio_request.h>
|
||||
|
||||
#include <gbinder_reader.h>
|
||||
#include <gbinder_writer.h>
|
||||
|
||||
#include <gutil_misc.h>
|
||||
|
||||
#define SEC (1000)
|
||||
#define USSD_REQUEST_TIMEOUT_MS (30 * SEC)
|
||||
#define USSD_CANCEL_TIMEOUT_MS (20 * SEC)
|
||||
|
||||
typedef struct binder_ussd {
|
||||
struct ofono_ussd *ussd;
|
||||
char* log_prefix;
|
||||
RadioClient* client;
|
||||
RadioRequest* send_req;
|
||||
RadioRequest* cancel_req;
|
||||
gulong event_id;
|
||||
guint register_id;
|
||||
} BinderUssd;
|
||||
|
||||
typedef struct binder_ussd_cbd {
|
||||
BinderUssd* self;
|
||||
ofono_ussd_cb_t cb;
|
||||
gpointer data;
|
||||
} BinderUssdCbData;
|
||||
|
||||
#define DBG_(cd,fmt,args...) DBG("%s" fmt, (cd)->log_prefix, ##args)
|
||||
|
||||
static inline BinderUssd* binder_ussd_get_data(struct ofono_ussd *ussd)
|
||||
{ return ofono_ussd_get_data(ussd); }
|
||||
|
||||
static
|
||||
BinderUssdCbData*
|
||||
binder_ussd_cbd_new(
|
||||
BinderUssd* self,
|
||||
ofono_ussd_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
BinderUssdCbData* cbd = g_slice_new(struct binder_ussd_cbd);
|
||||
|
||||
cbd->self = self;
|
||||
cbd->cb = cb;
|
||||
cbd->data = data;
|
||||
return cbd;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_ussd_cbd_free(
|
||||
gpointer cbd)
|
||||
{
|
||||
g_slice_free(BinderUssdCbData, cbd);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_ussd_cancel_cb(
|
||||
RadioRequest* req,
|
||||
RADIO_TX_STATUS status,
|
||||
RADIO_RESP resp,
|
||||
RADIO_ERROR error,
|
||||
const GBinderReader* args,
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderUssdCbData* cbd = user_data;
|
||||
BinderUssd* self = cbd->self;
|
||||
struct ofono_error err;
|
||||
|
||||
GASSERT(self->cancel_req == req);
|
||||
radio_request_unref(self->cancel_req);
|
||||
self->cancel_req = NULL;
|
||||
|
||||
if (status == RADIO_TX_STATUS_OK) {
|
||||
if (resp == RADIO_RESP_CANCEL_PENDING_USSD) {
|
||||
if (error != RADIO_ERROR_NONE) {
|
||||
ofono_warn("Error cancelling USSD: %s",
|
||||
binder_radio_error_string(error));
|
||||
}
|
||||
} else {
|
||||
ofono_error("Unexpected cancelPendingUssd response %d", resp);
|
||||
}
|
||||
} else {
|
||||
ofono_warn("Failed to cancel USSD");
|
||||
}
|
||||
|
||||
/*
|
||||
* Always report sucessful completion, otherwise ofono may get
|
||||
* stuck in the USSD_STATE_ACTIVE state.
|
||||
*/
|
||||
cbd->cb(binder_error_ok(&err), cbd->data);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_ussd_send_cb(
|
||||
RadioRequest* req,
|
||||
RADIO_TX_STATUS status,
|
||||
RADIO_RESP resp,
|
||||
RADIO_ERROR error,
|
||||
const GBinderReader* args,
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderUssdCbData* cbd = user_data;
|
||||
BinderUssd* self = cbd->self;
|
||||
struct ofono_error err;
|
||||
|
||||
GASSERT(self->send_req == req);
|
||||
radio_request_unref(self->send_req);
|
||||
self->send_req = NULL;
|
||||
|
||||
if (status == RADIO_TX_STATUS_OK) {
|
||||
if (resp == RADIO_RESP_SEND_USSD) {
|
||||
if (error == RADIO_ERROR_NONE) {
|
||||
cbd->cb(binder_error_ok(&err), cbd->data);
|
||||
return;
|
||||
} else {
|
||||
ofono_warn("Error sending USSD: %s",
|
||||
binder_radio_error_string(error));
|
||||
}
|
||||
} else {
|
||||
ofono_error("Unexpected sendUssd response %d", resp);
|
||||
}
|
||||
} else {
|
||||
ofono_warn("Failed to send USSD");
|
||||
}
|
||||
cbd->cb(binder_error_failure(&err), cbd->data);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_ussd_request(
|
||||
struct ofono_ussd* ussd,
|
||||
int dcs,
|
||||
const unsigned char* pdu,
|
||||
int len,
|
||||
ofono_ussd_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
BinderUssd* self = binder_ussd_get_data(ussd);
|
||||
char* text = ofono_ussd_decode(dcs, pdu, len);
|
||||
struct ofono_error err;
|
||||
|
||||
DBG_(self, "ussd request: %s", text);
|
||||
GASSERT(!self->send_req);
|
||||
radio_request_drop(self->send_req);
|
||||
self->send_req = NULL;
|
||||
|
||||
if (text) {
|
||||
/* sendUssd(int32 serial, string ussd); */
|
||||
GBinderWriter writer;
|
||||
RadioRequest* req = radio_request_new(self->client,
|
||||
RADIO_REQ_SEND_USSD, &writer,
|
||||
binder_ussd_send_cb, binder_ussd_cbd_free,
|
||||
binder_ussd_cbd_new(self, cb, data));
|
||||
|
||||
/* USSD text will be deallocated together with the request */
|
||||
gbinder_writer_append_hidl_string(&writer, text);
|
||||
gbinder_writer_add_cleanup(&writer, (GDestroyNotify)
|
||||
ofono_ussd_decode_free, text);
|
||||
|
||||
radio_request_set_timeout(req, USSD_REQUEST_TIMEOUT_MS);
|
||||
if (radio_request_submit(req)) {
|
||||
/* Request was successfully submitted, keep the ref */
|
||||
self->send_req = req;
|
||||
return;
|
||||
}
|
||||
|
||||
radio_request_unref(req);
|
||||
}
|
||||
|
||||
/* Something went wrong */
|
||||
cb(binder_error_failure(&err), data);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_ussd_cancel(
|
||||
struct ofono_ussd* ussd,
|
||||
ofono_ussd_cb_t cb,
|
||||
void* data)
|
||||
{
|
||||
BinderUssd* self = binder_ussd_get_data(ussd);
|
||||
|
||||
ofono_info("sending ussd cancel");
|
||||
GASSERT(!self->cancel_req);
|
||||
radio_request_drop(self->cancel_req);
|
||||
|
||||
/* cancelPendingUssd(int32 serial); */
|
||||
self->cancel_req = radio_request_new(self->client,
|
||||
RADIO_REQ_CANCEL_PENDING_USSD, NULL,
|
||||
binder_ussd_cancel_cb, binder_ussd_cbd_free,
|
||||
binder_ussd_cbd_new(self, cb, data));
|
||||
|
||||
radio_request_set_timeout(self->cancel_req, USSD_CANCEL_TIMEOUT_MS);
|
||||
if (!radio_request_submit(self->cancel_req)) {
|
||||
struct ofono_error err;
|
||||
|
||||
radio_request_unref(self->cancel_req);
|
||||
self->cancel_req = NULL;
|
||||
cb(binder_error_failure(&err), data);
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_ussd_notify(
|
||||
RadioClient* client,
|
||||
RADIO_IND code,
|
||||
const GBinderReader* args,
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderUssd* self = user_data;
|
||||
GBinderReader reader;
|
||||
gint32 type = 0;
|
||||
|
||||
ofono_info("ussd received");
|
||||
|
||||
/* onUssd(RadioIndicationType, UssdModeType modeType, string msg); */
|
||||
GASSERT(code == RADIO_IND_ON_USSD);
|
||||
gbinder_reader_copy(&reader, args);
|
||||
if (gbinder_reader_read_int32(&reader, &type)) {
|
||||
const char* msg = gbinder_reader_read_hidl_string_c(&reader);
|
||||
|
||||
if (msg && msg[0]) {
|
||||
const int len = (int) strlen(msg);
|
||||
|
||||
DBG_(self, "ussd length %d", len);
|
||||
|
||||
/*
|
||||
* Message is freed by core if dcs is 0xff, we have to
|
||||
* duplicate it.
|
||||
*/
|
||||
ofono_ussd_notify(self->ussd, type, 0xff,
|
||||
gutil_memdup(msg, len + 1), len);
|
||||
} else {
|
||||
ofono_ussd_notify(self->ussd, type, 0, NULL, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
gboolean
|
||||
binder_ussd_register(
|
||||
gpointer user_data)
|
||||
{
|
||||
BinderUssd* self = user_data;
|
||||
|
||||
DBG_(self, "");
|
||||
GASSERT(self->register_id);
|
||||
self->register_id = 0;
|
||||
|
||||
ofono_ussd_register(self->ussd);
|
||||
|
||||
/* Register for USSD events */
|
||||
self->event_id = radio_client_add_indication_handler(self->client,
|
||||
RADIO_IND_ON_USSD, binder_ussd_notify, self);
|
||||
|
||||
return G_SOURCE_REMOVE;
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
binder_ussd_probe(
|
||||
struct ofono_ussd* ussd,
|
||||
unsigned int vendor,
|
||||
void* data)
|
||||
{
|
||||
BinderModem* modem = binder_modem_get_data(data);
|
||||
BinderUssd* self = g_new0(BinderUssd, 1);
|
||||
|
||||
self->ussd = ussd;
|
||||
self->client = radio_client_ref(modem->client);
|
||||
self->log_prefix = binder_dup_prefix(modem->log_prefix);
|
||||
self->register_id = g_idle_add(binder_ussd_register, self);
|
||||
|
||||
DBG_(self, "");
|
||||
ofono_ussd_set_data(ussd, self);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_ussd_remove(
|
||||
struct ofono_ussd* ussd)
|
||||
{
|
||||
BinderUssd* self = binder_ussd_get_data(ussd);
|
||||
|
||||
DBG_(self, "");
|
||||
|
||||
if (self->register_id) {
|
||||
g_source_remove(self->register_id);
|
||||
}
|
||||
|
||||
radio_request_drop(self->send_req);
|
||||
radio_request_drop(self->cancel_req);
|
||||
radio_client_remove_handler(self->client, self->event_id);
|
||||
radio_client_unref(self->client);
|
||||
|
||||
g_free(self->log_prefix);
|
||||
g_free(self);
|
||||
|
||||
ofono_ussd_set_data(ussd, NULL);
|
||||
}
|
||||
|
||||
/*==========================================================================*
|
||||
* API
|
||||
*==========================================================================*/
|
||||
|
||||
static const struct ofono_ussd_driver binder_ussd_driver = {
|
||||
.name = BINDER_DRIVER,
|
||||
.probe = binder_ussd_probe,
|
||||
.remove = binder_ussd_remove,
|
||||
.request = binder_ussd_request,
|
||||
.cancel = binder_ussd_cancel
|
||||
};
|
||||
|
||||
void
|
||||
binder_ussd_init()
|
||||
{
|
||||
ofono_ussd_driver_register(&binder_ussd_driver);
|
||||
}
|
||||
|
||||
void
|
||||
binder_ussd_cleanup()
|
||||
{
|
||||
ofono_ussd_driver_unregister(&binder_ussd_driver);
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
37
src/binder_ussd.h
Normal file
37
src/binder_ussd.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef BINDER_USSD_H
|
||||
#define BINDER_USSD_H
|
||||
|
||||
#include "binder_types.h"
|
||||
|
||||
void
|
||||
binder_ussd_init(void)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_ussd_cleanup(void)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
#endif /* BINDER_USSD_H */
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
839
src/binder_util.c
Normal file
839
src/binder_util.c
Normal file
@@ -0,0 +1,839 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "binder_util.h"
|
||||
|
||||
#include <ofono/misc.h>
|
||||
#include <ofono/netreg.h>
|
||||
#include <ofono/log.h>
|
||||
|
||||
#include <radio_request.h>
|
||||
|
||||
#include <gbinder_reader.h>
|
||||
#include <gbinder_writer.h>
|
||||
|
||||
#include <gutil_idlepool.h>
|
||||
#include <gutil_misc.h>
|
||||
|
||||
static GUtilIdlePool* binder_util_pool = NULL;
|
||||
static const char binder_empty_str[] = "";
|
||||
static const char PROTO_IP_STR[] = "IP";
|
||||
static const char PROTO_IPV6_STR[] = "IPV6";
|
||||
static const char PROTO_IPV4V6_STR[] = "IPV4V6";
|
||||
|
||||
#define RADIO_ACCESS_FAMILY_GSM \
|
||||
(RAF_GSM|RAF_GPRS|RAF_EDGE)
|
||||
#define RADIO_ACCESS_FAMILY_UMTS \
|
||||
(RAF_UMTS|RAF_HSDPA|RAF_HSUPA|RAF_HSPA|RAF_HSPAP|RAF_TD_SCDMA|RAF_EHRPD)
|
||||
#define RADIO_ACCESS_FAMILY_LTE \
|
||||
(RAF_LTE|RAF_LTE_CA|RAF_EHRPD)
|
||||
|
||||
static
|
||||
const char*
|
||||
binder_pool_string(
|
||||
char* str)
|
||||
{
|
||||
GUtilIdlePool* pool = gutil_idle_pool_get(&binder_util_pool);
|
||||
|
||||
gutil_idle_pool_add(pool, str, g_free);
|
||||
return str;
|
||||
}
|
||||
|
||||
RADIO_ACCESS_NETWORK
|
||||
binder_radio_access_network_for_tech(
|
||||
RADIO_TECH tech)
|
||||
{
|
||||
switch (tech) {
|
||||
case RADIO_TECH_GPRS:
|
||||
case RADIO_TECH_EDGE:
|
||||
case RADIO_TECH_GSM:
|
||||
return RADIO_ACCESS_NETWORK_GERAN;
|
||||
case RADIO_TECH_UMTS:
|
||||
case RADIO_TECH_HSDPA:
|
||||
case RADIO_TECH_HSPAP:
|
||||
case RADIO_TECH_HSUPA:
|
||||
case RADIO_TECH_HSPA:
|
||||
case RADIO_TECH_TD_SCDMA:
|
||||
return RADIO_ACCESS_NETWORK_UTRAN;
|
||||
case RADIO_TECH_IS95A:
|
||||
case RADIO_TECH_IS95B:
|
||||
case RADIO_TECH_ONE_X_RTT:
|
||||
case RADIO_TECH_EVDO_0:
|
||||
case RADIO_TECH_EVDO_A:
|
||||
case RADIO_TECH_EVDO_B:
|
||||
case RADIO_TECH_EHRPD:
|
||||
return RADIO_ACCESS_NETWORK_CDMA2000;
|
||||
case RADIO_TECH_LTE:
|
||||
case RADIO_TECH_LTE_CA:
|
||||
return RADIO_ACCESS_NETWORK_EUTRAN;
|
||||
case RADIO_TECH_IWLAN:
|
||||
return RADIO_ACCESS_NETWORK_IWLAN;
|
||||
case RADIO_TECH_UNKNOWN:
|
||||
break;
|
||||
}
|
||||
return RADIO_ACCESS_NETWORK_UNKNOWN;
|
||||
}
|
||||
|
||||
RADIO_APN_TYPES
|
||||
binder_radio_apn_types_for_profile(
|
||||
RADIO_DATA_PROFILE_ID profile_id)
|
||||
{
|
||||
switch (profile_id) {
|
||||
case RADIO_DATA_PROFILE_INVALID:
|
||||
return RADIO_APN_TYPE_NONE;
|
||||
case RADIO_DATA_PROFILE_IMS:
|
||||
return RADIO_APN_TYPE_IMS;
|
||||
case RADIO_DATA_PROFILE_CBS:
|
||||
return RADIO_APN_TYPE_CBS;
|
||||
case RADIO_DATA_PROFILE_FOTA:
|
||||
return RADIO_APN_TYPE_FOTA;
|
||||
case RADIO_DATA_PROFILE_DEFAULT:
|
||||
return (RADIO_APN_TYPE_DEFAULT |
|
||||
RADIO_APN_TYPE_SUPL |
|
||||
RADIO_APN_TYPE_IA);
|
||||
default:
|
||||
/*
|
||||
* There's no standard profile id for MMS, OEM-specific profile ids
|
||||
* are used for that.
|
||||
*/
|
||||
return RADIO_APN_TYPE_MMS;
|
||||
}
|
||||
}
|
||||
|
||||
RADIO_PDP_PROTOCOL_TYPE
|
||||
binder_proto_from_ofono_proto(
|
||||
enum ofono_gprs_proto proto)
|
||||
{
|
||||
switch (proto) {
|
||||
case OFONO_GPRS_PROTO_IP:
|
||||
return RADIO_PDP_PROTOCOL_IP;
|
||||
case OFONO_GPRS_PROTO_IPV6:
|
||||
return RADIO_PDP_PROTOCOL_IPV6;
|
||||
case OFONO_GPRS_PROTO_IPV4V6:
|
||||
return RADIO_PDP_PROTOCOL_IPV4V6;
|
||||
}
|
||||
return RADIO_PDP_PROTOCOL_UNKNOWN;
|
||||
}
|
||||
|
||||
const char*
|
||||
binder_proto_str_from_ofono_proto(
|
||||
enum ofono_gprs_proto proto)
|
||||
{
|
||||
switch (proto) {
|
||||
case OFONO_GPRS_PROTO_IP:
|
||||
return PROTO_IP_STR;
|
||||
case OFONO_GPRS_PROTO_IPV6:
|
||||
return PROTO_IPV6_STR;
|
||||
case OFONO_GPRS_PROTO_IPV4V6:
|
||||
return PROTO_IPV4V6_STR;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
enum ofono_gprs_proto
|
||||
binder_ofono_proto_from_proto_type(
|
||||
RADIO_PDP_PROTOCOL_TYPE type)
|
||||
{
|
||||
switch (type) {
|
||||
case RADIO_PDP_PROTOCOL_IP:
|
||||
return OFONO_GPRS_PROTO_IP;
|
||||
case RADIO_PDP_PROTOCOL_IPV6:
|
||||
return OFONO_GPRS_PROTO_IPV6;
|
||||
case RADIO_PDP_PROTOCOL_IPV4V6:
|
||||
return OFONO_GPRS_PROTO_IPV4V6;
|
||||
case RADIO_PDP_PROTOCOL_PPP:
|
||||
case RADIO_PDP_PROTOCOL_NON_IP:
|
||||
case RADIO_PDP_PROTOCOL_UNSTRUCTURED:
|
||||
case RADIO_PDP_PROTOCOL_UNKNOWN:
|
||||
break;
|
||||
}
|
||||
/* Need OFONO_GPRS_PROTO_UNKNOWN? */
|
||||
return OFONO_GPRS_PROTO_IP;
|
||||
}
|
||||
|
||||
enum ofono_gprs_proto
|
||||
binder_ofono_proto_from_proto_str(
|
||||
const char* type)
|
||||
{
|
||||
if (type) {
|
||||
if (!g_ascii_strcasecmp(type, PROTO_IP_STR)) {
|
||||
return OFONO_GPRS_PROTO_IP;
|
||||
} else if (!g_ascii_strcasecmp(type, PROTO_IPV6_STR)) {
|
||||
return OFONO_GPRS_PROTO_IPV6;
|
||||
} else if (!g_ascii_strcasecmp(type, PROTO_IPV4V6_STR)) {
|
||||
return OFONO_GPRS_PROTO_IPV4V6;
|
||||
}
|
||||
}
|
||||
/* Need OFONO_GPRS_PROTO_UNKNOWN? */
|
||||
return OFONO_GPRS_PROTO_IP;
|
||||
}
|
||||
|
||||
RADIO_APN_AUTH_TYPE
|
||||
binder_radio_auth_from_ofono_method(
|
||||
enum ofono_gprs_auth_method auth)
|
||||
{
|
||||
switch (auth) {
|
||||
case OFONO_GPRS_AUTH_METHOD_NONE:
|
||||
return RADIO_APN_AUTH_NONE;
|
||||
case OFONO_GPRS_AUTH_METHOD_CHAP:
|
||||
return RADIO_APN_AUTH_CHAP;
|
||||
case OFONO_GPRS_AUTH_METHOD_PAP:
|
||||
return RADIO_APN_AUTH_PAP;
|
||||
case OFONO_GPRS_AUTH_METHOD_ANY:
|
||||
/* Use default */
|
||||
break;
|
||||
}
|
||||
/* Default */
|
||||
return RADIO_APN_AUTH_PAP_CHAP;
|
||||
}
|
||||
|
||||
RADIO_PREF_NET_TYPE
|
||||
binder_pref_from_raf(
|
||||
RADIO_ACCESS_FAMILY raf)
|
||||
{
|
||||
if (raf & RADIO_ACCESS_FAMILY_GSM) {
|
||||
if (raf & RADIO_ACCESS_FAMILY_UMTS) {
|
||||
if (raf & RADIO_ACCESS_FAMILY_LTE) {
|
||||
return RADIO_PREF_NET_LTE_GSM_WCDMA;
|
||||
}
|
||||
return RADIO_PREF_NET_GSM_WCDMA;
|
||||
}
|
||||
return RADIO_PREF_NET_GSM_ONLY;
|
||||
} else if (raf & RADIO_ACCESS_FAMILY_UMTS) {
|
||||
if (raf & RADIO_ACCESS_FAMILY_LTE) {
|
||||
return RADIO_PREF_NET_LTE_WCDMA;
|
||||
}
|
||||
return RADIO_PREF_NET_WCDMA;
|
||||
} else if (raf & RADIO_ACCESS_FAMILY_LTE) {
|
||||
return RADIO_PREF_NET_LTE_ONLY;
|
||||
} else {
|
||||
return RADIO_PREF_NET_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
int
|
||||
binder_pref_mask(
|
||||
RADIO_PREF_NET_TYPE pref,
|
||||
int none,
|
||||
int gsm_mask,
|
||||
int umts_mask,
|
||||
int lte_mask)
|
||||
{
|
||||
switch (pref) {
|
||||
case RADIO_PREF_NET_GSM_ONLY:
|
||||
return gsm_mask;
|
||||
|
||||
case RADIO_PREF_NET_WCDMA:
|
||||
case RADIO_PREF_NET_TD_SCDMA_ONLY:
|
||||
case RADIO_PREF_NET_TD_SCDMA_WCDMA:
|
||||
return umts_mask;
|
||||
|
||||
case RADIO_PREF_NET_LTE_ONLY:
|
||||
case RADIO_PREF_NET_LTE_CDMA_EVDO:
|
||||
return lte_mask;
|
||||
|
||||
case RADIO_PREF_NET_TD_SCDMA_GSM:
|
||||
case RADIO_PREF_NET_GSM_WCDMA:
|
||||
case RADIO_PREF_NET_GSM_WCDMA_AUTO:
|
||||
case RADIO_PREF_NET_GSM_WCDMA_CDMA_EVDO_AUTO:
|
||||
case RADIO_PREF_NET_TD_SCDMA_GSM_WCDMA:
|
||||
case RADIO_PREF_NET_TD_SCDMA_GSM_WCDMA_CDMA_EVDO_AUTO:
|
||||
return gsm_mask | umts_mask;
|
||||
|
||||
case RADIO_PREF_NET_LTE_WCDMA:
|
||||
case RADIO_PREF_NET_TD_SCDMA_LTE:
|
||||
case RADIO_PREF_NET_TD_SCDMA_WCDMA_LTE:
|
||||
return umts_mask | lte_mask;
|
||||
|
||||
case RADIO_PREF_NET_LTE_GSM_WCDMA:
|
||||
case RADIO_PREF_NET_TD_SCDMA_GSM_LTE:
|
||||
case RADIO_PREF_NET_LTE_CMDA_EVDO_GSM_WCDMA:
|
||||
case RADIO_PREF_NET_TD_SCDMA_GSM_WCDMA_LTE:
|
||||
case RADIO_PREF_NET_TD_SCDMA_LTE_CDMA_EVDO_GSM_WCDMA:
|
||||
return gsm_mask | umts_mask | lte_mask;
|
||||
|
||||
case RADIO_PREF_NET_CDMA_ONLY:
|
||||
case RADIO_PREF_NET_EVDO_ONLY:
|
||||
case RADIO_PREF_NET_CDMA_EVDO_AUTO:
|
||||
case RADIO_PREF_NET_INVALID:
|
||||
return none;
|
||||
}
|
||||
|
||||
DBG("unexpected pref mode %d", pref);
|
||||
return none;
|
||||
}
|
||||
|
||||
RADIO_ACCESS_FAMILY
|
||||
binder_raf_from_pref(
|
||||
RADIO_PREF_NET_TYPE pref)
|
||||
{
|
||||
return binder_pref_mask(pref, RAF_NONE,
|
||||
RADIO_ACCESS_FAMILY_GSM, RADIO_ACCESS_FAMILY_UMTS,
|
||||
RADIO_ACCESS_FAMILY_LTE);
|
||||
}
|
||||
|
||||
enum ofono_radio_access_mode
|
||||
binder_access_modes_from_pref(
|
||||
RADIO_PREF_NET_TYPE pref)
|
||||
{
|
||||
return binder_pref_mask(pref, OFONO_RADIO_ACCESS_MODE_NONE,
|
||||
OFONO_RADIO_ACCESS_MODE_GSM, OFONO_RADIO_ACCESS_MODE_UMTS,
|
||||
OFONO_RADIO_ACCESS_MODE_LTE);
|
||||
}
|
||||
|
||||
enum ofono_radio_access_mode
|
||||
binder_access_modes_from_raf(
|
||||
RADIO_ACCESS_FAMILY raf)
|
||||
{
|
||||
|
||||
if (raf == RAF_UNKNOWN) {
|
||||
return OFONO_RADIO_ACCESS_MODE_ALL;
|
||||
} else {
|
||||
enum ofono_radio_access_mode modes = OFONO_RADIO_ACCESS_MODE_NONE;
|
||||
|
||||
if (raf & RADIO_ACCESS_FAMILY_GSM) {
|
||||
modes |= OFONO_RADIO_ACCESS_MODE_GSM;
|
||||
}
|
||||
if (raf & RADIO_ACCESS_FAMILY_UMTS) {
|
||||
modes |= OFONO_RADIO_ACCESS_MODE_UMTS;
|
||||
}
|
||||
if (raf & RADIO_ACCESS_FAMILY_LTE) {
|
||||
modes |= OFONO_RADIO_ACCESS_MODE_LTE;
|
||||
}
|
||||
return modes;
|
||||
}
|
||||
}
|
||||
|
||||
enum ofono_radio_access_mode
|
||||
binder_access_modes_up_to(
|
||||
enum ofono_radio_access_mode mode)
|
||||
{
|
||||
/* Make sure only one bit is set in max_mode */
|
||||
enum ofono_radio_access_mode max_mode = ofono_radio_access_max_mode(mode);
|
||||
|
||||
/* Treat ANY (zero) as ALL, otherwise set all lower bits */
|
||||
return (max_mode == OFONO_RADIO_ACCESS_MODE_ANY) ?
|
||||
OFONO_RADIO_ACCESS_MODE_ALL : (max_mode | (max_mode - 1));
|
||||
}
|
||||
|
||||
enum ofono_access_technology
|
||||
binder_access_tech_from_radio_tech(
|
||||
RADIO_TECH radio_tech)
|
||||
{
|
||||
switch (radio_tech) {
|
||||
case RADIO_TECH_UNKNOWN:
|
||||
return OFONO_ACCESS_TECHNOLOGY_NONE;
|
||||
case RADIO_TECH_GPRS:
|
||||
case RADIO_TECH_GSM:
|
||||
return OFONO_ACCESS_TECHNOLOGY_GSM;
|
||||
case RADIO_TECH_EDGE:
|
||||
return OFONO_ACCESS_TECHNOLOGY_GSM_EGPRS;
|
||||
case RADIO_TECH_UMTS:
|
||||
return OFONO_ACCESS_TECHNOLOGY_UTRAN;
|
||||
case RADIO_TECH_HSDPA:
|
||||
return OFONO_ACCESS_TECHNOLOGY_UTRAN_HSDPA;
|
||||
case RADIO_TECH_HSUPA:
|
||||
return OFONO_ACCESS_TECHNOLOGY_UTRAN_HSUPA;
|
||||
case RADIO_TECH_HSPA:
|
||||
case RADIO_TECH_HSPAP:
|
||||
return OFONO_ACCESS_TECHNOLOGY_UTRAN_HSDPA_HSUPA;
|
||||
case RADIO_TECH_LTE:
|
||||
case RADIO_TECH_LTE_CA:
|
||||
return OFONO_ACCESS_TECHNOLOGY_EUTRAN;
|
||||
case RADIO_TECH_IWLAN:
|
||||
case RADIO_TECH_IS95B:
|
||||
case RADIO_TECH_ONE_X_RTT:
|
||||
case RADIO_TECH_EVDO_0:
|
||||
case RADIO_TECH_EVDO_A:
|
||||
case RADIO_TECH_EVDO_B:
|
||||
case RADIO_TECH_EHRPD:
|
||||
case RADIO_TECH_TD_SCDMA:
|
||||
case RADIO_TECH_IS95A:
|
||||
break;
|
||||
}
|
||||
|
||||
DBG("Unknown radio tech %d", radio_tech);
|
||||
return OFONO_ACCESS_TECHNOLOGY_NONE;
|
||||
}
|
||||
|
||||
const char*
|
||||
binder_ofono_access_technology_string(
|
||||
enum ofono_access_technology act)
|
||||
{
|
||||
switch (act) {
|
||||
case OFONO_ACCESS_TECHNOLOGY_NONE:
|
||||
return "none";
|
||||
case OFONO_ACCESS_TECHNOLOGY_GSM:
|
||||
return "gsm";
|
||||
case OFONO_ACCESS_TECHNOLOGY_GSM_COMPACT:
|
||||
return "gsmc";
|
||||
case OFONO_ACCESS_TECHNOLOGY_UTRAN:
|
||||
return "utran";
|
||||
case OFONO_ACCESS_TECHNOLOGY_GSM_EGPRS:
|
||||
return "egprs";
|
||||
case OFONO_ACCESS_TECHNOLOGY_UTRAN_HSDPA:
|
||||
return "hsdpa";
|
||||
case OFONO_ACCESS_TECHNOLOGY_UTRAN_HSUPA:
|
||||
return "hsupa";
|
||||
case OFONO_ACCESS_TECHNOLOGY_UTRAN_HSDPA_HSUPA:
|
||||
return "utran";
|
||||
case OFONO_ACCESS_TECHNOLOGY_EUTRAN:
|
||||
return "eutran";
|
||||
}
|
||||
return binder_pool_string(g_strdup_printf("%d (?)", act));
|
||||
}
|
||||
|
||||
const char*
|
||||
binder_radio_op_status_string(
|
||||
RADIO_OP_STATUS status)
|
||||
{
|
||||
switch (status) {
|
||||
case RADIO_OP_AVAILABLE:
|
||||
return "available";
|
||||
case RADIO_OP_CURRENT:
|
||||
return "current";
|
||||
case RADIO_OP_FORBIDDEN:
|
||||
return "forbidden";
|
||||
case RADIO_OP_STATUS_UNKNOWN:
|
||||
break;
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
const char*
|
||||
binder_radio_state_string(
|
||||
RADIO_STATE state)
|
||||
{
|
||||
#define RADIO_STATE_(name) case RADIO_STATE_##name: return #name
|
||||
switch (state) {
|
||||
RADIO_STATE_(OFF);
|
||||
RADIO_STATE_(UNAVAILABLE);
|
||||
RADIO_STATE_(ON);
|
||||
}
|
||||
return binder_pool_string(g_strdup_printf("%d (?)", state));
|
||||
}
|
||||
|
||||
const char*
|
||||
binder_radio_error_string(
|
||||
RADIO_ERROR error)
|
||||
{
|
||||
switch (error) {
|
||||
#define RADIO_ERROR_STR_(name) case RADIO_ERROR_##name: return #name
|
||||
RADIO_ERROR_STR_(NONE);
|
||||
RADIO_ERROR_STR_(RADIO_NOT_AVAILABLE);
|
||||
RADIO_ERROR_STR_(GENERIC_FAILURE);
|
||||
RADIO_ERROR_STR_(PASSWORD_INCORRECT);
|
||||
RADIO_ERROR_STR_(SIM_PIN2);
|
||||
RADIO_ERROR_STR_(SIM_PUK2);
|
||||
RADIO_ERROR_STR_(REQUEST_NOT_SUPPORTED);
|
||||
RADIO_ERROR_STR_(CANCELLED);
|
||||
RADIO_ERROR_STR_(OP_NOT_ALLOWED_DURING_VOICE_CALL);
|
||||
RADIO_ERROR_STR_(OP_NOT_ALLOWED_BEFORE_REG_TO_NW);
|
||||
RADIO_ERROR_STR_(SMS_SEND_FAIL_RETRY);
|
||||
RADIO_ERROR_STR_(SIM_ABSENT);
|
||||
RADIO_ERROR_STR_(SUBSCRIPTION_NOT_AVAILABLE);
|
||||
RADIO_ERROR_STR_(MODE_NOT_SUPPORTED);
|
||||
RADIO_ERROR_STR_(FDN_CHECK_FAILURE);
|
||||
RADIO_ERROR_STR_(ILLEGAL_SIM_OR_ME);
|
||||
RADIO_ERROR_STR_(MISSING_RESOURCE);
|
||||
RADIO_ERROR_STR_(NO_SUCH_ELEMENT);
|
||||
RADIO_ERROR_STR_(DIAL_MODIFIED_TO_USSD);
|
||||
RADIO_ERROR_STR_(DIAL_MODIFIED_TO_SS);
|
||||
RADIO_ERROR_STR_(DIAL_MODIFIED_TO_DIAL);
|
||||
RADIO_ERROR_STR_(USSD_MODIFIED_TO_DIAL);
|
||||
RADIO_ERROR_STR_(USSD_MODIFIED_TO_SS);
|
||||
RADIO_ERROR_STR_(USSD_MODIFIED_TO_USSD);
|
||||
RADIO_ERROR_STR_(SS_MODIFIED_TO_DIAL);
|
||||
RADIO_ERROR_STR_(SS_MODIFIED_TO_USSD);
|
||||
RADIO_ERROR_STR_(SUBSCRIPTION_NOT_SUPPORTED);
|
||||
RADIO_ERROR_STR_(SS_MODIFIED_TO_SS);
|
||||
RADIO_ERROR_STR_(LCE_NOT_SUPPORTED);
|
||||
RADIO_ERROR_STR_(NO_MEMORY);
|
||||
RADIO_ERROR_STR_(INTERNAL_ERR);
|
||||
RADIO_ERROR_STR_(SYSTEM_ERR);
|
||||
RADIO_ERROR_STR_(MODEM_ERR);
|
||||
RADIO_ERROR_STR_(INVALID_STATE);
|
||||
RADIO_ERROR_STR_(NO_RESOURCES);
|
||||
RADIO_ERROR_STR_(SIM_ERR);
|
||||
RADIO_ERROR_STR_(INVALID_ARGUMENTS);
|
||||
RADIO_ERROR_STR_(INVALID_SIM_STATE);
|
||||
RADIO_ERROR_STR_(INVALID_MODEM_STATE);
|
||||
RADIO_ERROR_STR_(INVALID_CALL_ID);
|
||||
RADIO_ERROR_STR_(NO_SMS_TO_ACK);
|
||||
RADIO_ERROR_STR_(NETWORK_ERR);
|
||||
RADIO_ERROR_STR_(REQUEST_RATE_LIMITED);
|
||||
RADIO_ERROR_STR_(SIM_BUSY);
|
||||
RADIO_ERROR_STR_(SIM_FULL);
|
||||
RADIO_ERROR_STR_(NETWORK_REJECT);
|
||||
RADIO_ERROR_STR_(OPERATION_NOT_ALLOWED);
|
||||
RADIO_ERROR_STR_(EMPTY_RECORD);
|
||||
RADIO_ERROR_STR_(INVALID_SMS_FORMAT);
|
||||
RADIO_ERROR_STR_(ENCODING_ERR);
|
||||
RADIO_ERROR_STR_(INVALID_SMSC_ADDRESS);
|
||||
RADIO_ERROR_STR_(NO_SUCH_ENTRY);
|
||||
RADIO_ERROR_STR_(NETWORK_NOT_READY);
|
||||
RADIO_ERROR_STR_(NOT_PROVISIONED);
|
||||
RADIO_ERROR_STR_(NO_SUBSCRIPTION);
|
||||
RADIO_ERROR_STR_(NO_NETWORK_FOUND);
|
||||
RADIO_ERROR_STR_(DEVICE_IN_USE);
|
||||
RADIO_ERROR_STR_(ABORTED);
|
||||
RADIO_ERROR_STR_(INVALID_RESPONSE);
|
||||
RADIO_ERROR_STR_(OEM_ERROR_1);
|
||||
RADIO_ERROR_STR_(OEM_ERROR_2);
|
||||
RADIO_ERROR_STR_(OEM_ERROR_3);
|
||||
RADIO_ERROR_STR_(OEM_ERROR_4);
|
||||
RADIO_ERROR_STR_(OEM_ERROR_5);
|
||||
RADIO_ERROR_STR_(OEM_ERROR_6);
|
||||
RADIO_ERROR_STR_(OEM_ERROR_7);
|
||||
RADIO_ERROR_STR_(OEM_ERROR_8);
|
||||
RADIO_ERROR_STR_(OEM_ERROR_9);
|
||||
RADIO_ERROR_STR_(OEM_ERROR_10);
|
||||
RADIO_ERROR_STR_(OEM_ERROR_11);
|
||||
RADIO_ERROR_STR_(OEM_ERROR_12);
|
||||
RADIO_ERROR_STR_(OEM_ERROR_13);
|
||||
RADIO_ERROR_STR_(OEM_ERROR_14);
|
||||
RADIO_ERROR_STR_(OEM_ERROR_15);
|
||||
RADIO_ERROR_STR_(OEM_ERROR_16);
|
||||
RADIO_ERROR_STR_(OEM_ERROR_17);
|
||||
RADIO_ERROR_STR_(OEM_ERROR_18);
|
||||
RADIO_ERROR_STR_(OEM_ERROR_19);
|
||||
RADIO_ERROR_STR_(OEM_ERROR_20);
|
||||
RADIO_ERROR_STR_(OEM_ERROR_21);
|
||||
RADIO_ERROR_STR_(OEM_ERROR_22);
|
||||
RADIO_ERROR_STR_(OEM_ERROR_23);
|
||||
RADIO_ERROR_STR_(OEM_ERROR_24);
|
||||
RADIO_ERROR_STR_(OEM_ERROR_25);
|
||||
}
|
||||
|
||||
return binder_pool_string(g_strdup_printf("%d", error));
|
||||
}
|
||||
|
||||
enum ofono_access_technology
|
||||
binder_parse_tech(
|
||||
const char* stech,
|
||||
RADIO_TECH* radio_tech)
|
||||
{
|
||||
int rt = RADIO_TECH_UNKNOWN;
|
||||
const enum ofono_access_technology at = gutil_parse_int(stech, 0, &rt) ?
|
||||
binder_access_tech_from_radio_tech(rt) : OFONO_ACCESS_TECHNOLOGY_NONE;
|
||||
|
||||
if (radio_tech) {
|
||||
*radio_tech = rt;
|
||||
}
|
||||
return at;
|
||||
}
|
||||
|
||||
gboolean
|
||||
binder_parse_mcc_mnc(
|
||||
const char* str,
|
||||
struct ofono_network_operator* op)
|
||||
{
|
||||
if (str) {
|
||||
int i;
|
||||
const char* ptr = str;
|
||||
|
||||
/* Three digit country code */
|
||||
for (i = 0;
|
||||
i < OFONO_MAX_MCC_LENGTH && *ptr && g_ascii_isdigit(*ptr);
|
||||
i++) {
|
||||
op->mcc[i] = *ptr++;
|
||||
}
|
||||
op->mcc[i] = 0;
|
||||
|
||||
if (i == OFONO_MAX_MCC_LENGTH) {
|
||||
/* Usually 2 but sometimes 3 digit network code */
|
||||
for (i = 0;
|
||||
i < OFONO_MAX_MNC_LENGTH && *ptr && g_ascii_isdigit(*ptr);
|
||||
i++) {
|
||||
op->mnc[i] = *ptr++;
|
||||
}
|
||||
op->mnc[i] = 0;
|
||||
|
||||
if (i > 0) {
|
||||
/*
|
||||
* Sometimes MCC/MNC are followed by + and what looks
|
||||
* like the technology code. This seems to be modem
|
||||
* specific.
|
||||
*/
|
||||
if (*ptr == '+') {
|
||||
const enum ofono_access_technology at =
|
||||
binder_parse_tech(ptr + 1, NULL);
|
||||
|
||||
if (at != OFONO_ACCESS_TECHNOLOGY_NONE) {
|
||||
op->tech = at;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
char*
|
||||
binder_encode_hex(
|
||||
const void* in,
|
||||
guint size)
|
||||
{
|
||||
char *out = g_new(char, size * 2 + 1);
|
||||
|
||||
ofono_encode_hex(in, size, out);
|
||||
return out;
|
||||
}
|
||||
|
||||
void*
|
||||
binder_decode_hex(
|
||||
const char* hex,
|
||||
int len,
|
||||
guint* out_size)
|
||||
{
|
||||
void* out = NULL;
|
||||
guint size = 0;
|
||||
|
||||
if (hex) {
|
||||
if (len < 0) {
|
||||
len = (int) strlen(hex);
|
||||
}
|
||||
if (len > 0 && !(len & 1)) {
|
||||
size = len/2;
|
||||
out = g_malloc(size);
|
||||
if (!gutil_hex2bin(hex, len, out)) {
|
||||
g_free(out);
|
||||
out = NULL;
|
||||
size = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (out_size) {
|
||||
*out_size = size;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
const char*
|
||||
binder_print_strv(
|
||||
char** strv,
|
||||
const char* sep)
|
||||
{
|
||||
if (!strv) {
|
||||
return NULL;
|
||||
} else if (!strv[0]) {
|
||||
return binder_empty_str;
|
||||
} else {
|
||||
GUtilIdlePool* pool = gutil_idle_pool_get(&binder_util_pool);
|
||||
char* str = g_strjoinv(sep, strv);
|
||||
|
||||
gutil_idle_pool_add(pool, str, g_free);
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
const char*
|
||||
binder_print_hex(
|
||||
const void* data,
|
||||
gsize size)
|
||||
{
|
||||
if (data && size) {
|
||||
const guint8* bytes = data;
|
||||
GUtilIdlePool* pool = gutil_idle_pool_get(&binder_util_pool);
|
||||
char* str = g_new(char, size * 2 + 1);
|
||||
char* ptr = str;
|
||||
gsize i;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
static const char hex[] = "0123456789abcdef";
|
||||
const guint8 b = bytes[i];
|
||||
|
||||
*ptr++ = hex[(b >> 4) & 0xf];
|
||||
*ptr++ = hex[b & 0xf];
|
||||
}
|
||||
*ptr++ = 0;
|
||||
gutil_idle_pool_add(pool, str, g_free);
|
||||
return str;
|
||||
}
|
||||
return binder_empty_str;
|
||||
}
|
||||
|
||||
gboolean
|
||||
binder_submit_request(
|
||||
RadioRequestGroup* g,
|
||||
RADIO_REQ code)
|
||||
{
|
||||
RadioRequest* req = radio_request_new2(g, code, NULL, NULL, NULL, NULL);
|
||||
gboolean ok = radio_request_submit(req);
|
||||
|
||||
radio_request_unref(req);
|
||||
return ok;
|
||||
}
|
||||
|
||||
gboolean
|
||||
binder_submit_request2(
|
||||
RadioRequestGroup* g,
|
||||
RADIO_REQ code,
|
||||
RadioRequestCompleteFunc complete,
|
||||
GDestroyNotify destroy,
|
||||
void* user_data)
|
||||
{
|
||||
RadioRequest* req = radio_request_new2(g, code, NULL, complete, destroy,
|
||||
user_data);
|
||||
gboolean ok = radio_request_submit(req);
|
||||
|
||||
radio_request_unref(req);
|
||||
return ok;
|
||||
}
|
||||
|
||||
const char*
|
||||
binder_read_hidl_string(
|
||||
const GBinderReader* args)
|
||||
{
|
||||
GBinderReader reader;
|
||||
|
||||
/* Read a single string arg */
|
||||
gbinder_reader_copy(&reader, args);
|
||||
return gbinder_reader_read_hidl_string_c(&reader);
|
||||
}
|
||||
|
||||
gboolean
|
||||
binder_read_int32(
|
||||
const GBinderReader* args,
|
||||
gint32* value)
|
||||
{
|
||||
GBinderReader reader;
|
||||
|
||||
/* Read a single int32 arg */
|
||||
gbinder_reader_copy(&reader, args);
|
||||
return gbinder_reader_read_int32(&reader, value);
|
||||
}
|
||||
|
||||
const void*
|
||||
binder_read_hidl_struct1(
|
||||
const GBinderReader* args,
|
||||
gsize size)
|
||||
{
|
||||
GBinderReader reader;
|
||||
|
||||
/* Read a single struct */
|
||||
gbinder_reader_copy(&reader, args);
|
||||
return gbinder_reader_read_hidl_struct1(&reader, size);
|
||||
}
|
||||
|
||||
char**
|
||||
binder_strv_from_hidl_string_vec(
|
||||
const GBinderHidlVec* vec)
|
||||
{
|
||||
if (vec) {
|
||||
const GBinderHidlString* strings = vec->data.ptr;
|
||||
char** out = g_new(char*, vec->count);
|
||||
char** ptr = out;
|
||||
guint i;
|
||||
|
||||
for (i = 0; i < vec->count; i++, ptr++) {
|
||||
const char* str = strings[i].data.str;
|
||||
|
||||
*ptr = str ? gutil_memdup(str, strings[i].len + 1) : g_strdup("");
|
||||
}
|
||||
*ptr = NULL;
|
||||
return out;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
guint
|
||||
binder_append_vec_with_data(
|
||||
GBinderWriter* writer,
|
||||
const void* data,
|
||||
guint elemsize,
|
||||
guint count,
|
||||
const GBinderParent* parent)
|
||||
{
|
||||
GBinderHidlVec* vec = gbinder_writer_new0(writer, GBinderHidlVec);
|
||||
GBinderParent p;
|
||||
|
||||
vec->data.ptr = data;
|
||||
vec->count = count;
|
||||
|
||||
p.index = gbinder_writer_append_buffer_object(writer, vec, sizeof(*vec));
|
||||
p.offset = GBINDER_HIDL_VEC_BUFFER_OFFSET;
|
||||
|
||||
/* Return the index of the data buffer */
|
||||
return gbinder_writer_append_buffer_object_with_parent(writer,
|
||||
data, count * elemsize, &p);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
binder_copy_hidl_string_impl(
|
||||
GBinderWriter* writer,
|
||||
GBinderHidlString* dest,
|
||||
const char* src,
|
||||
gssize len)
|
||||
{
|
||||
dest->owns_buffer = TRUE;
|
||||
if (len > 0) {
|
||||
/* GBinderWriter takes ownership of the string contents */
|
||||
dest->len = (guint32) len;
|
||||
dest->data.str = gbinder_writer_memdup(writer, src, len + 1);
|
||||
} else {
|
||||
/* Replace NULL strings with empty strings */
|
||||
dest->data.str = binder_empty_str;
|
||||
dest->len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
binder_copy_hidl_string(
|
||||
GBinderWriter* writer,
|
||||
GBinderHidlString* dest,
|
||||
const char* src)
|
||||
{
|
||||
binder_copy_hidl_string_impl(writer, dest, src, src ? strlen(src) : 0);
|
||||
}
|
||||
|
||||
void
|
||||
binder_copy_hidl_string_len(
|
||||
GBinderWriter* writer,
|
||||
GBinderHidlString* dest,
|
||||
const char* src,
|
||||
gssize len)
|
||||
{
|
||||
binder_copy_hidl_string_impl(writer, dest, src, (src && len < 0) ?
|
||||
strlen(src) : 0);
|
||||
}
|
||||
|
||||
void
|
||||
binder_append_hidl_string_with_parent(
|
||||
GBinderWriter* writer,
|
||||
const GBinderHidlString* str,
|
||||
guint32 index,
|
||||
guint32 offset)
|
||||
{
|
||||
GBinderParent parent;
|
||||
|
||||
parent.index = index;
|
||||
parent.offset = offset;
|
||||
|
||||
/* Strings are NULL-terminated, hence len + 1 */
|
||||
gbinder_writer_append_buffer_object_with_parent(writer, str->data.str,
|
||||
str->len + 1, &parent);
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
256
src/binder_util.h
Normal file
256
src/binder_util.h
Normal file
@@ -0,0 +1,256 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef BINDER_UTIL_H
|
||||
#define BINDER_UTIL_H
|
||||
|
||||
#include "binder_types.h"
|
||||
|
||||
#include <ofono/gprs.h>
|
||||
#include <ofono/radio-settings.h>
|
||||
|
||||
#include <radio_request.h>
|
||||
|
||||
struct ofono_network_operator;
|
||||
|
||||
#define binder_dup_prefix(prefix) \
|
||||
(((prefix) && *(prefix)) ? (g_str_has_suffix(prefix, " ") ? \
|
||||
g_strdup(prefix) : g_strconcat(prefix, " ", NULL)) : g_strdup(""))
|
||||
|
||||
#define binder_error_init_ok(err) \
|
||||
((err)->error = 0, (err)->type = OFONO_ERROR_TYPE_NO_ERROR)
|
||||
#define binder_error_init_failure(err) \
|
||||
((err)->error = 0, (err)->type = OFONO_ERROR_TYPE_FAILURE)
|
||||
#define binder_error_init_sim_error(err,sw1,sw2) \
|
||||
((err)->error = (((guint)(sw1)) << 8) | (sw2), \
|
||||
(err)->type = OFONO_ERROR_TYPE_SIM)
|
||||
|
||||
#define binder_error_ok(err) \
|
||||
(binder_error_init_ok(err), err)
|
||||
#define binder_error_failure(err) \
|
||||
(binder_error_init_failure(err), err)
|
||||
#define binder_error_sim(err,sw1,sw2) \
|
||||
(binder_error_init_sim_error(err,sw1,sw2), err)
|
||||
|
||||
RADIO_ACCESS_NETWORK
|
||||
binder_radio_access_network_for_tech(
|
||||
RADIO_TECH tech)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
RADIO_APN_TYPES
|
||||
binder_radio_apn_types_for_profile(
|
||||
RADIO_DATA_PROFILE_ID profile_id)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
RADIO_PDP_PROTOCOL_TYPE
|
||||
binder_proto_from_ofono_proto(
|
||||
enum ofono_gprs_proto proto)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
const char*
|
||||
binder_proto_str_from_ofono_proto(
|
||||
enum ofono_gprs_proto proto)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
enum ofono_gprs_proto
|
||||
binder_ofono_proto_from_proto_type(
|
||||
RADIO_PDP_PROTOCOL_TYPE type)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
enum ofono_gprs_proto
|
||||
binder_ofono_proto_from_proto_str(
|
||||
const char* type)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
RADIO_APN_AUTH_TYPE
|
||||
binder_radio_auth_from_ofono_method(
|
||||
enum ofono_gprs_auth_method auth)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
RADIO_PREF_NET_TYPE
|
||||
binder_pref_from_raf(
|
||||
RADIO_ACCESS_FAMILY raf)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
RADIO_ACCESS_FAMILY
|
||||
binder_raf_from_pref(
|
||||
RADIO_PREF_NET_TYPE pref)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
enum ofono_radio_access_mode
|
||||
binder_access_modes_from_pref(
|
||||
RADIO_PREF_NET_TYPE pref)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
enum ofono_radio_access_mode
|
||||
binder_access_modes_from_raf(
|
||||
RADIO_ACCESS_FAMILY raf)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
enum ofono_radio_access_mode
|
||||
binder_access_modes_up_to(
|
||||
enum ofono_radio_access_mode mode)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
enum ofono_access_technology
|
||||
binder_access_tech_from_radio_tech(
|
||||
RADIO_TECH radio_tech)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
const char*
|
||||
binder_ofono_access_technology_string(
|
||||
enum ofono_access_technology tech)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
const char*
|
||||
binder_radio_op_status_string(
|
||||
RADIO_OP_STATUS status)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
const char*
|
||||
binder_radio_state_string(
|
||||
RADIO_STATE state)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
const char*
|
||||
binder_radio_error_string(
|
||||
RADIO_ERROR error)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
enum ofono_access_technology
|
||||
binder_parse_tech(
|
||||
const char* stech,
|
||||
RADIO_TECH* radio_tech)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
gboolean
|
||||
binder_parse_mcc_mnc(
|
||||
const char* str,
|
||||
struct ofono_network_operator* op)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
char*
|
||||
binder_encode_hex(
|
||||
const void* in,
|
||||
guint size)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void*
|
||||
binder_decode_hex(
|
||||
const char* hex,
|
||||
int len,
|
||||
guint* out_size)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
const char*
|
||||
binder_print_strv(
|
||||
char** strv,
|
||||
const char* sep)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
const char*
|
||||
binder_print_hex(
|
||||
const void* data,
|
||||
gsize size)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
gboolean
|
||||
binder_submit_request(
|
||||
RadioRequestGroup* g,
|
||||
RADIO_REQ code)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
gboolean
|
||||
binder_submit_request2(
|
||||
RadioRequestGroup* g,
|
||||
RADIO_REQ code,
|
||||
RadioRequestCompleteFunc complete,
|
||||
GDestroyNotify destroy,
|
||||
void* user_data)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
const char*
|
||||
binder_read_hidl_string(
|
||||
const GBinderReader* args)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
gboolean
|
||||
binder_read_int32(
|
||||
const GBinderReader* args,
|
||||
gint32* value)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
const void*
|
||||
binder_read_hidl_struct1(
|
||||
const GBinderReader* reader,
|
||||
gsize size);
|
||||
|
||||
#define binder_read_hidl_struct(reader,type) \
|
||||
((const type*)binder_read_hidl_struct1(reader, sizeof(type)))
|
||||
|
||||
char**
|
||||
binder_strv_from_hidl_string_vec(
|
||||
const GBinderHidlVec* vec)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
guint
|
||||
binder_append_vec_with_data(
|
||||
GBinderWriter* writer,
|
||||
const void* data,
|
||||
guint elemsize,
|
||||
guint count,
|
||||
const GBinderParent* parent)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_copy_hidl_string(
|
||||
GBinderWriter* writer,
|
||||
GBinderHidlString* dest,
|
||||
const char* src)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_copy_hidl_string_len(
|
||||
GBinderWriter* writer,
|
||||
GBinderHidlString* dest,
|
||||
const char* src,
|
||||
gssize len)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_append_hidl_string_with_parent(
|
||||
GBinderWriter* writer,
|
||||
const GBinderHidlString* str,
|
||||
guint32 index,
|
||||
guint32 offset)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
#define binder_append_hidl_string(writer,str) \
|
||||
gbinder_writer_append_hidl_string_copy(writer,str)
|
||||
#define binder_append_hidl_string_data(writer,ptr,field,index) \
|
||||
binder_append_hidl_string_data2(writer,ptr,field,index,0)
|
||||
#define binder_append_hidl_string_data2(writer,ptr,field,index,off) \
|
||||
binder_append_hidl_string_with_parent(writer, &ptr->field, index, \
|
||||
(off) + ((guint8*)(&ptr->field) - (guint8*)ptr))
|
||||
|
||||
#endif /* BINDER_UTIL_H */
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
1199
src/binder_voicecall.c
Normal file
1199
src/binder_voicecall.c
Normal file
File diff suppressed because it is too large
Load Diff
37
src/binder_voicecall.h
Normal file
37
src/binder_voicecall.h
Normal file
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021-2022 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef BINDER_VOICECALL_H
|
||||
#define BINDER_VOICECALL_H
|
||||
|
||||
#include "binder_types.h"
|
||||
|
||||
void
|
||||
binder_voicecall_init(void)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
void
|
||||
binder_voicecall_cleanup(void)
|
||||
BINDER_INTERNAL;
|
||||
|
||||
#endif /* BINDER_VOICECALL_H */
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
12
unit/Makefile
Normal file
12
unit/Makefile
Normal file
@@ -0,0 +1,12 @@
|
||||
# -*- Mode: makefile-gmake -*-
|
||||
|
||||
.PHONY: clean all
|
||||
|
||||
all:
|
||||
%:
|
||||
@$(MAKE) -C unit_base $*
|
||||
@$(MAKE) -C unit_sim_settings $*
|
||||
|
||||
clean: unitclean
|
||||
rm -f coverage/*.gcov
|
||||
rm -fr coverage/report
|
||||
206
unit/common/Makefile
Normal file
206
unit/common/Makefile
Normal file
@@ -0,0 +1,206 @@
|
||||
# -*- Mode: makefile-gmake -*-
|
||||
|
||||
.PHONY: clean cleaner unitclean all debug release coverage valgrind
|
||||
.PHONY: debug_lib release_lib coverage_lib
|
||||
.PHONY: test test_banner
|
||||
|
||||
#
|
||||
# Real test makefile defines EXE (and possibly SRC) and includes this one.
|
||||
#
|
||||
|
||||
ifndef EXE
|
||||
${error EXE not defined}
|
||||
endif
|
||||
|
||||
SRC ?= $(EXE).c
|
||||
# COMMON_SRC += test_main.c
|
||||
|
||||
#
|
||||
# Required packages
|
||||
#
|
||||
|
||||
LINK_PKGS += libglibutil glib-2.0 gobject-2.0
|
||||
PKGS += $(LINK_PKGS) libgbinder libgbinder-radio
|
||||
|
||||
#
|
||||
# Default target
|
||||
#
|
||||
|
||||
all: debug release
|
||||
|
||||
#
|
||||
# Directories
|
||||
#
|
||||
|
||||
SRC_DIR = .
|
||||
LIB_DIR = ../..
|
||||
COMMON_DIR = ../common
|
||||
BUILD_DIR = build
|
||||
DEBUG_BUILD_DIR = $(BUILD_DIR)/debug
|
||||
RELEASE_BUILD_DIR = $(BUILD_DIR)/release
|
||||
COVERAGE_BUILD_DIR = $(BUILD_DIR)/coverage
|
||||
COMMON_BUILD_DIR = $(COMMON_DIR)/build
|
||||
COMMON_DEBUG_BUILD_DIR = $(COMMON_BUILD_DIR)/debug
|
||||
COMMON_RELEASE_BUILD_DIR = $(COMMON_BUILD_DIR)/release
|
||||
COMMON_COVERAGE_BUILD_DIR = $(COMMON_BUILD_DIR)/coverage
|
||||
|
||||
#
|
||||
# Tools and flags
|
||||
#
|
||||
|
||||
CC ?= $(CROSS_COMPILE)gcc
|
||||
LD = $(CC)
|
||||
WARNINGS += -Wall -Wno-deprecated-declarations
|
||||
INCLUDES += -I$(COMMON_DIR) -I$(LIB_DIR)/src -I$(LIB_DIR)/include
|
||||
BASE_FLAGS = -fPIC
|
||||
FULL_CFLAGS = $(BASE_FLAGS) $(CFLAGS) $(DEFINES) $(WARNINGS) $(INCLUDES) \
|
||||
-MMD -MP $(shell pkg-config --cflags $(PKGS))
|
||||
FULL_LDFLAGS = $(BASE_FLAGS) $(LDFLAGS)
|
||||
LIBS = $(shell pkg-config --libs $(LINK_PKGS)) -lpthread
|
||||
QUIET_MAKE = make --no-print-directory
|
||||
DEBUG_FLAGS = -g
|
||||
RELEASE_FLAGS =
|
||||
COVERAGE_FLAGS = -g
|
||||
|
||||
DEBUG_LDFLAGS = $(FULL_LDFLAGS) $(DEBUG_FLAGS)
|
||||
RELEASE_LDFLAGS = $(FULL_LDFLAGS) $(RELEASE_FLAGS)
|
||||
COVERAGE_LDFLAGS = $(FULL_LDFLAGS) $(COVERAGE_FLAGS) --coverage
|
||||
|
||||
DEBUG_CFLAGS = $(FULL_CFLAGS) $(DEBUG_FLAGS) -DDEBUG
|
||||
RELEASE_CFLAGS = $(FULL_CFLAGS) $(RELEASE_FLAGS) -O2
|
||||
COVERAGE_CFLAGS = $(FULL_CFLAGS) $(COVERAGE_FLAGS) --coverage
|
||||
|
||||
DEBUG_LIB_FILE := $(shell $(QUIET_MAKE) -C $(LIB_DIR) print_debug_lib)
|
||||
RELEASE_LIB_FILE := $(shell $(QUIET_MAKE) -C $(LIB_DIR) print_release_lib)
|
||||
COVERAGE_LIB_FILE := $(shell $(QUIET_MAKE) -C $(LIB_DIR) print_coverage_lib)
|
||||
|
||||
DEBUG_LIB = $(LIB_DIR)/$(DEBUG_LIB_FILE)
|
||||
RELEASE_LIB = $(LIB_DIR)/$(RELEASE_LIB_FILE)
|
||||
COVERAGE_LIB = $(LIB_DIR)/$(COVERAGE_LIB_FILE)
|
||||
|
||||
DEBUG_LIBS = $(DEBUG_LIB) $(LIBS)
|
||||
RELEASE_LIBS = $(RELEASE_LIB) $(LIBS)
|
||||
COVERAGE_LIBS = $(COVERAGE_LIB) $(LIBS)
|
||||
|
||||
#
|
||||
# Files
|
||||
#
|
||||
|
||||
TEST_DEBUG_OBJS = $(SRC:%.c=$(DEBUG_BUILD_DIR)/%.o)
|
||||
TEST_RELEASE_OBJS = $(SRC:%.c=$(RELEASE_BUILD_DIR)/%.o)
|
||||
TEST_COVERAGE_OBJS = $(SRC:%.c=$(COVERAGE_BUILD_DIR)/%.o)
|
||||
COMMON_DEBUG_OBJS = $(COMMON_SRC:%.c=$(COMMON_DEBUG_BUILD_DIR)/%.o)
|
||||
COMMON_RELEASE_OBJS = $(COMMON_SRC:%.c=$(COMMON_RELEASE_BUILD_DIR)/%.o)
|
||||
COMMON_COVERAGE_OBJS = $(COMMON_SRC:%.c=$(COMMON_COVERAGE_BUILD_DIR)/%.o)
|
||||
DEBUG_OBJS = $(COMMON_DEBUG_OBJS) $(TEST_DEBUG_OBJS)
|
||||
RELEASE_OBJS = $(COMMON_RELEASE_OBJS) $(TEST_RELEASE_OBJS)
|
||||
COVERAGE_OBJS = $(COMMON_COVERAGE_OBJS) $(TEST_COVERAGE_OBJS)
|
||||
|
||||
#
|
||||
# Dependencies
|
||||
#
|
||||
|
||||
DEPS = $(DEBUG_OBJS:%.o=%.d) $(RELEASE_OBJS:%.o=%.d) $(COVERAGE_OBJS:%.o=%.d)
|
||||
ifneq ($(MAKECMDGOALS),clean)
|
||||
ifneq ($(strip $(DEPS)),)
|
||||
-include $(DEPS)
|
||||
endif
|
||||
endif
|
||||
|
||||
$(DEBUG_LIB): | debug_lib
|
||||
$(RELEASE_LIB): | release_lib
|
||||
$(COVERAGE_LIB): | coverage_lib
|
||||
|
||||
$(TEST_DEBUG_OBJS): | $(DEBUG_BUILD_DIR)
|
||||
$(TEST_RELEASE_OBJS): | $(RELEASE_BUILD_DIR)
|
||||
$(TEST_COVERAGE_OBJS): | $(COVERAGE_BUILD_DIR)
|
||||
|
||||
$(COMMON_DEBUG_OBJS): | $(COMMON_DEBUG_BUILD_DIR)
|
||||
$(COMMON_RELEASE_OBJS): | $(COMMON_RELEASE_BUILD_DIR)
|
||||
$(COMMON_COVERAGE_OBJS): | $(COMMON_COVERAGE_BUILD_DIR)
|
||||
|
||||
#
|
||||
# Rules
|
||||
#
|
||||
|
||||
DEBUG_EXE = $(DEBUG_BUILD_DIR)/$(EXE)
|
||||
RELEASE_EXE = $(RELEASE_BUILD_DIR)/$(EXE)
|
||||
COVERAGE_EXE = $(COVERAGE_BUILD_DIR)/$(EXE)
|
||||
|
||||
debug: debug_lib $(DEBUG_EXE)
|
||||
|
||||
release: release_lib $(RELEASE_EXE)
|
||||
|
||||
coverage: coverage_lib $(COVERAGE_EXE)
|
||||
|
||||
unitclean:
|
||||
rm -f *~
|
||||
rm -fr $(BUILD_DIR) $(COMMON_BUILD_DIR)
|
||||
|
||||
clean: unitclean
|
||||
|
||||
cleaner: unitclean
|
||||
@make -C $(LIB_DIR) clean
|
||||
|
||||
test_banner:
|
||||
@echo "===========" $(EXE) "=========== "
|
||||
|
||||
test: test_banner debug
|
||||
@$(DEBUG_EXE)
|
||||
|
||||
valgrind: test_banner debug
|
||||
@G_DEBUG=gc-friendly G_SLICE=always-malloc valgrind --tool=memcheck --leak-check=full --show-possibly-lost=no $(DEBUG_EXE)
|
||||
|
||||
$(DEBUG_BUILD_DIR):
|
||||
mkdir -p $@
|
||||
|
||||
$(RELEASE_BUILD_DIR):
|
||||
mkdir -p $@
|
||||
|
||||
$(COVERAGE_BUILD_DIR):
|
||||
mkdir -p $@
|
||||
|
||||
$(COMMON_DEBUG_BUILD_DIR):
|
||||
mkdir -p $@
|
||||
|
||||
$(COMMON_RELEASE_BUILD_DIR):
|
||||
mkdir -p $@
|
||||
|
||||
$(COMMON_COVERAGE_BUILD_DIR):
|
||||
mkdir -p $@
|
||||
|
||||
$(DEBUG_BUILD_DIR)/%.o : $(SRC_DIR)/%.c
|
||||
$(CC) -c $(DEBUG_CFLAGS) -MT"$@" -MF"$(@:%.o=%.d)" $< -o $@
|
||||
|
||||
$(RELEASE_BUILD_DIR)/%.o : $(SRC_DIR)/%.c
|
||||
$(CC) -c $(RELEASE_CFLAGS) -MT"$@" -MF"$(@:%.o=%.d)" $< -o $@
|
||||
|
||||
$(COVERAGE_BUILD_DIR)/%.o : $(SRC_DIR)/%.c
|
||||
$(CC) -c $(COVERAGE_CFLAGS) -MT"$@" -MF"$(@:%.o=%.d)" $< -o $@
|
||||
|
||||
$(COMMON_DEBUG_BUILD_DIR)/%.o : $(COMMON_DIR)/%.c
|
||||
$(CC) -c $(DEBUG_CFLAGS) -MT"$@" -MF"$(@:%.o=%.d)" $< -o $@
|
||||
|
||||
$(COMMON_RELEASE_BUILD_DIR)/%.o : $(COMMON_DIR)/%.c
|
||||
$(CC) -c $(RELEASE_CFLAGS) -MT"$@" -MF"$(@:%.o=%.d)" $< -o $@
|
||||
|
||||
$(COMMON_COVERAGE_BUILD_DIR)/%.o : $(COMMON_DIR)/%.c
|
||||
$(CC) -c $(COVERAGE_CFLAGS) -MT"$@" -MF"$(@:%.o=%.d)" $< -o $@
|
||||
|
||||
$(DEBUG_EXE): $(DEBUG_LIB) $(DEBUG_OBJS)
|
||||
$(LD) $(DEBUG_LDFLAGS) $(DEBUG_OBJS) $(DEBUG_LIBS) -o $@
|
||||
|
||||
$(RELEASE_EXE): $(RELEASE_LIB) $(RELEASE_OBJS)
|
||||
$(LD) $(RELEASE_LDFLAGS) $(RELEASE_OBJS) $(RELEASE_LIBS) -o $@
|
||||
|
||||
$(COVERAGE_EXE): $(COVERAG_LIB) $(COVERAGE_OBJS)
|
||||
$(LD) $(COVERAGE_LDFLAGS) $(COVERAGE_OBJS) $(COVERAGE_LIBS) -o $@
|
||||
|
||||
debug_lib:
|
||||
$(MAKE) -C $(LIB_DIR) $@
|
||||
|
||||
release_lib:
|
||||
$(MAKE) -C $(LIB_DIR) $@
|
||||
|
||||
coverage_lib:
|
||||
$(MAKE) -C $(LIB_DIR) $@
|
||||
426
unit/common/test_watch.c
Normal file
426
unit/common/test_watch.c
Normal file
@@ -0,0 +1,426 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony
|
||||
*
|
||||
* Copyright (C) 2017-2021 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "test_watch.h"
|
||||
|
||||
#include <gutil_log.h>
|
||||
#include <gutil_macros.h>
|
||||
#include <gutil_misc.h>
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
typedef GObjectClass TestOfonoWatchClass;
|
||||
|
||||
typedef struct test_ofono_watch {
|
||||
GObject object;
|
||||
OfonoWatch pub;
|
||||
char* path;
|
||||
char* iccid;
|
||||
char* imsi;
|
||||
char* spn;
|
||||
int queued_signals;
|
||||
} TestOfonoWatch;
|
||||
|
||||
typedef struct test_ofono_watch_closure {
|
||||
GCClosure cclosure;
|
||||
ofono_watch_cb_t cb;
|
||||
void* user_data;
|
||||
} TestOfonoWatchClosure;
|
||||
|
||||
#define SIGNAL_MODEM_CHANGED_NAME "ofono-watch-modem-changed"
|
||||
#define SIGNAL_ONLINE_CHANGED_NAME "ofono-watch-online-changed"
|
||||
#define SIGNAL_SIM_CHANGED_NAME "ofono-watch-sim-changed"
|
||||
#define SIGNAL_SIM_STATE_CHANGED_NAME "ofono-watch-sim-state-changed"
|
||||
#define SIGNAL_ICCID_CHANGED_NAME "ofono-watch-iccid-changed"
|
||||
#define SIGNAL_IMSI_CHANGED_NAME "ofono-watch-imsi-changed"
|
||||
#define SIGNAL_SPN_CHANGED_NAME "ofono-watch-spn-changed"
|
||||
#define SIGNAL_NETREG_CHANGED_NAME "ofono-watch-netreg-changed"
|
||||
|
||||
static guint test_ofono_watch_signals[TEST_WATCH_SIGNAL_COUNT] = { 0 };
|
||||
static GHashTable* test_ofono_watch_table = NULL;
|
||||
|
||||
G_DEFINE_TYPE(TestOfonoWatch, test_ofono_watch, G_TYPE_OBJECT)
|
||||
#define PARENT_CLASS test_ofono_watch_parent_class
|
||||
#define THIS_TYPE test_ofono_watch_get_type()
|
||||
#define THIS(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, THIS_TYPE, TestOfonoWatch)
|
||||
|
||||
#define NEW_SIGNAL(klass,name) \
|
||||
test_ofono_watch_signals[TEST_WATCH_SIGNAL_##name##_CHANGED] = \
|
||||
g_signal_new(SIGNAL_##name##_CHANGED_NAME, \
|
||||
G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_FIRST, \
|
||||
0, NULL, NULL, NULL, G_TYPE_NONE, 0)
|
||||
|
||||
static inline TestOfonoWatch* test_ofono_watch_cast(struct ofono_watch* watch)
|
||||
{ return watch ? THIS(G_CAST(watch, TestOfonoWatch, pub)) : NULL; }
|
||||
|
||||
static inline int test_ofono_watch_signal_bit(TEST_WATCH_SIGNAL id)
|
||||
{ return (1 << id); }
|
||||
|
||||
static
|
||||
void
|
||||
test_ofono_watch_signal_emit(
|
||||
TestOfonoWatch* self,
|
||||
TEST_WATCH_SIGNAL id)
|
||||
{
|
||||
self->queued_signals &= ~test_ofono_watch_signal_bit(id);
|
||||
g_signal_emit(self, test_ofono_watch_signals[id], 0);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
test_ofono_watch_destroyed(
|
||||
gpointer key,
|
||||
GObject* obj)
|
||||
{
|
||||
GASSERT(test_ofono_watch_table);
|
||||
GDEBUG("TestOfonoWatch %s destroyed", (char*) key);
|
||||
if (test_ofono_watch_table) {
|
||||
GASSERT(g_hash_table_lookup(test_ofono_watch_table,key) == obj);
|
||||
g_hash_table_remove(test_ofono_watch_table, key);
|
||||
if (g_hash_table_size(test_ofono_watch_table) == 0) {
|
||||
g_hash_table_unref(test_ofono_watch_table);
|
||||
test_ofono_watch_table = NULL;
|
||||
GDEBUG("Last TestOfonoWatch is gone");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
test_watch_signal_cb(
|
||||
TestOfonoWatch* source,
|
||||
TestOfonoWatchClosure* closure)
|
||||
{
|
||||
closure->cb(&source->pub, closure->user_data);
|
||||
}
|
||||
|
||||
static
|
||||
unsigned long
|
||||
test_watch_add_signal_handler(
|
||||
OfonoWatch* watch,
|
||||
TEST_WATCH_SIGNAL signal,
|
||||
ofono_watch_cb_t cb,
|
||||
void* user_data)
|
||||
{
|
||||
if (watch && cb) {
|
||||
TestOfonoWatch* self = test_ofono_watch_cast(watch);
|
||||
TestOfonoWatchClosure* closure = (TestOfonoWatchClosure*)
|
||||
g_closure_new_simple(sizeof(TestOfonoWatchClosure), NULL);
|
||||
|
||||
closure->cclosure.closure.data = closure;
|
||||
closure->cclosure.callback = G_CALLBACK(test_watch_signal_cb);
|
||||
closure->cb = cb;
|
||||
closure->user_data = user_data;
|
||||
|
||||
return g_signal_connect_closure_by_id(self, test_ofono_watch_signals
|
||||
[signal], 0, &closure->cclosure.closure, FALSE);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
test_ofono_watch_init(
|
||||
TestOfonoWatch* self)
|
||||
{
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
test_ofono_watch_finalize(
|
||||
GObject* object)
|
||||
{
|
||||
TestOfonoWatch* self = THIS(object);
|
||||
|
||||
g_free(self->path);
|
||||
g_free(self->iccid);
|
||||
g_free(self->imsi);
|
||||
g_free(self->spn);
|
||||
G_OBJECT_CLASS(PARENT_CLASS)->finalize(object);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
test_ofono_watch_class_init(
|
||||
TestOfonoWatchClass* klass)
|
||||
{
|
||||
G_OBJECT_CLASS(klass)->finalize = test_ofono_watch_finalize;
|
||||
NEW_SIGNAL(klass, MODEM);
|
||||
NEW_SIGNAL(klass, ONLINE);
|
||||
NEW_SIGNAL(klass, SIM);
|
||||
NEW_SIGNAL(klass, SIM_STATE);
|
||||
NEW_SIGNAL(klass, ICCID);
|
||||
NEW_SIGNAL(klass, IMSI);
|
||||
NEW_SIGNAL(klass, SPN);
|
||||
NEW_SIGNAL(klass, NETREG);
|
||||
}
|
||||
|
||||
/*==========================================================================*
|
||||
* Test API
|
||||
*==========================================================================*/
|
||||
|
||||
void
|
||||
test_watch_signal_queue(
|
||||
OfonoWatch* watch,
|
||||
TEST_WATCH_SIGNAL id)
|
||||
{
|
||||
TestOfonoWatch* self = test_ofono_watch_cast(watch);
|
||||
|
||||
self->queued_signals |= test_ofono_watch_signal_bit(id);
|
||||
}
|
||||
|
||||
void
|
||||
test_watch_emit_queued_signals(
|
||||
OfonoWatch* watch)
|
||||
{
|
||||
TestOfonoWatch* self = test_ofono_watch_cast(watch);
|
||||
int i;
|
||||
|
||||
for (i = 0; self->queued_signals && i < TEST_WATCH_SIGNAL_COUNT; i++) {
|
||||
if (self->queued_signals & test_ofono_watch_signal_bit(i)) {
|
||||
test_ofono_watch_signal_emit(self, i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
test_watch_set_ofono_iccid(
|
||||
OfonoWatch* watch,
|
||||
const char* iccid)
|
||||
{
|
||||
TestOfonoWatch* self = test_ofono_watch_cast(watch);
|
||||
|
||||
if (g_strcmp0(self->iccid, iccid)) {
|
||||
g_free(self->iccid);
|
||||
watch->iccid = self->iccid = g_strdup(iccid);
|
||||
test_watch_signal_queue(watch, TEST_WATCH_SIGNAL_ICCID_CHANGED);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
test_watch_set_ofono_imsi(
|
||||
OfonoWatch* watch,
|
||||
const char* imsi)
|
||||
{
|
||||
TestOfonoWatch* self = test_ofono_watch_cast(watch);
|
||||
|
||||
if (g_strcmp0(self->imsi, imsi)) {
|
||||
g_free(self->imsi);
|
||||
watch->imsi = self->imsi = g_strdup(imsi);
|
||||
test_watch_signal_queue(watch, TEST_WATCH_SIGNAL_IMSI_CHANGED);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
test_watch_set_ofono_spn(
|
||||
OfonoWatch* watch,
|
||||
const char* spn)
|
||||
{
|
||||
TestOfonoWatch* self = test_ofono_watch_cast(watch);
|
||||
|
||||
if (g_strcmp0(self->spn, spn)) {
|
||||
g_free(self->spn);
|
||||
watch->spn = self->spn = g_strdup(spn);
|
||||
test_watch_signal_queue(watch, TEST_WATCH_SIGNAL_SPN_CHANGED);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
test_watch_set_ofono_sim(
|
||||
OfonoWatch* watch,
|
||||
struct ofono_sim* sim)
|
||||
{
|
||||
if (watch->sim != sim) {
|
||||
watch->sim = sim;
|
||||
test_watch_signal_queue(watch, TEST_WATCH_SIGNAL_SIM_CHANGED);
|
||||
if (!sim) {
|
||||
test_watch_set_ofono_iccid(watch, NULL);
|
||||
test_watch_set_ofono_imsi(watch, NULL);
|
||||
test_watch_set_ofono_spn(watch, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
test_watch_set_ofono_netreg(
|
||||
OfonoWatch* watch,
|
||||
struct ofono_netreg* netreg)
|
||||
{
|
||||
if (watch->netreg != netreg) {
|
||||
watch->netreg = netreg;
|
||||
test_watch_signal_queue(watch, TEST_WATCH_SIGNAL_NETREG_CHANGED);
|
||||
}
|
||||
}
|
||||
|
||||
/*==========================================================================*
|
||||
* Ofono API
|
||||
*==========================================================================*/
|
||||
|
||||
OfonoWatch*
|
||||
ofono_watch_new(
|
||||
const char* path)
|
||||
{
|
||||
if (path) {
|
||||
TestOfonoWatch* self = NULL;
|
||||
|
||||
if (test_ofono_watch_table) {
|
||||
self = g_hash_table_lookup(test_ofono_watch_table, path);
|
||||
}
|
||||
if (self) {
|
||||
g_object_ref(self);
|
||||
} else {
|
||||
char* key = g_strdup(path);
|
||||
|
||||
self = g_object_new(THIS_TYPE, NULL);
|
||||
self->pub.path = self->path = g_strdup(path);
|
||||
if (!test_ofono_watch_table) {
|
||||
/* Create the table on demand */
|
||||
test_ofono_watch_table = g_hash_table_new_full(g_str_hash,
|
||||
g_str_equal, g_free, NULL);
|
||||
}
|
||||
g_hash_table_replace(test_ofono_watch_table, key, self);
|
||||
g_object_weak_ref(G_OBJECT(self), test_ofono_watch_destroyed, key);
|
||||
GDEBUG("TestOfonoWatch %s created", path);
|
||||
}
|
||||
return &self->pub;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
OfonoWatch*
|
||||
ofono_watch_ref(
|
||||
OfonoWatch* watch)
|
||||
{
|
||||
if (watch) {
|
||||
g_object_ref(test_ofono_watch_cast(watch));
|
||||
}
|
||||
return watch;
|
||||
}
|
||||
|
||||
void
|
||||
ofono_watch_unref(
|
||||
OfonoWatch* watch)
|
||||
{
|
||||
if (watch) {
|
||||
g_object_unref(test_ofono_watch_cast(watch));
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long
|
||||
ofono_watch_add_modem_changed_handler(
|
||||
OfonoWatch* watch,
|
||||
ofono_watch_cb_t cb,
|
||||
void* user_data)
|
||||
{
|
||||
return test_watch_add_signal_handler(watch,
|
||||
TEST_WATCH_SIGNAL_MODEM_CHANGED, cb, user_data);
|
||||
}
|
||||
|
||||
unsigned long
|
||||
ofono_watch_add_online_changed_handler(
|
||||
OfonoWatch* watch,
|
||||
ofono_watch_cb_t cb,
|
||||
void* user_data)
|
||||
{
|
||||
return test_watch_add_signal_handler(watch,
|
||||
TEST_WATCH_SIGNAL_ONLINE_CHANGED, cb, user_data);
|
||||
}
|
||||
|
||||
unsigned long
|
||||
ofono_watch_add_sim_changed_handler(
|
||||
OfonoWatch* watch,
|
||||
ofono_watch_cb_t cb,
|
||||
void* user_data)
|
||||
{
|
||||
return test_watch_add_signal_handler(watch,
|
||||
TEST_WATCH_SIGNAL_SIM_CHANGED, cb, user_data);
|
||||
}
|
||||
|
||||
unsigned long
|
||||
ofono_watch_add_sim_state_changed_handler(
|
||||
OfonoWatch* watch,
|
||||
ofono_watch_cb_t cb,
|
||||
void* user_data)
|
||||
{
|
||||
return test_watch_add_signal_handler(watch,
|
||||
TEST_WATCH_SIGNAL_SIM_STATE_CHANGED, cb, user_data);
|
||||
}
|
||||
|
||||
unsigned long
|
||||
ofono_watch_add_iccid_changed_handler(
|
||||
OfonoWatch* watch,
|
||||
ofono_watch_cb_t cb,
|
||||
void* user_data)
|
||||
{
|
||||
return test_watch_add_signal_handler(watch,
|
||||
TEST_WATCH_SIGNAL_ICCID_CHANGED, cb, user_data);
|
||||
}
|
||||
|
||||
unsigned long
|
||||
ofono_watch_add_imsi_changed_handler(
|
||||
OfonoWatch* watch,
|
||||
ofono_watch_cb_t cb,
|
||||
void* user_data)
|
||||
{
|
||||
return test_watch_add_signal_handler(watch,
|
||||
TEST_WATCH_SIGNAL_IMSI_CHANGED, cb, user_data);
|
||||
}
|
||||
|
||||
unsigned long
|
||||
ofono_watch_add_spn_changed_handler(
|
||||
OfonoWatch* watch,
|
||||
ofono_watch_cb_t cb,
|
||||
void* user_data)
|
||||
{
|
||||
return test_watch_add_signal_handler(watch,
|
||||
TEST_WATCH_SIGNAL_SPN_CHANGED, cb, user_data);
|
||||
}
|
||||
|
||||
unsigned long
|
||||
ofono_watch_add_netreg_changed_handler(
|
||||
OfonoWatch* watch,
|
||||
ofono_watch_cb_t cb,
|
||||
void* user_data)
|
||||
{
|
||||
return test_watch_add_signal_handler(watch,
|
||||
TEST_WATCH_SIGNAL_NETREG_CHANGED, cb, user_data);
|
||||
}
|
||||
|
||||
void
|
||||
ofono_watch_remove_handler(
|
||||
OfonoWatch* watch,
|
||||
unsigned long id)
|
||||
{
|
||||
if (watch && id) {
|
||||
g_signal_handler_disconnect(test_ofono_watch_cast(watch), id);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ofono_watch_remove_handlers(
|
||||
OfonoWatch* watch,
|
||||
unsigned long* ids,
|
||||
unsigned int count)
|
||||
{
|
||||
gutil_disconnect_handlers(test_ofono_watch_cast(watch), ids, count);
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
77
unit/common/test_watch.h
Normal file
77
unit/common/test_watch.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony
|
||||
*
|
||||
* Copyright (C) 2017-2021 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef TEST_WATCH_H
|
||||
#define TEST_WATCH_H
|
||||
|
||||
#include <ofono/watch.h>
|
||||
|
||||
typedef enum test_watch_signal {
|
||||
TEST_WATCH_SIGNAL_MODEM_CHANGED,
|
||||
TEST_WATCH_SIGNAL_ONLINE_CHANGED,
|
||||
TEST_WATCH_SIGNAL_SIM_CHANGED,
|
||||
TEST_WATCH_SIGNAL_SIM_STATE_CHANGED,
|
||||
TEST_WATCH_SIGNAL_ICCID_CHANGED,
|
||||
TEST_WATCH_SIGNAL_IMSI_CHANGED,
|
||||
TEST_WATCH_SIGNAL_SPN_CHANGED,
|
||||
TEST_WATCH_SIGNAL_NETREG_CHANGED,
|
||||
TEST_WATCH_SIGNAL_COUNT
|
||||
} TEST_WATCH_SIGNAL;
|
||||
|
||||
typedef struct ofono_watch OfonoWatch;
|
||||
|
||||
void
|
||||
test_watch_signal_queue(
|
||||
OfonoWatch* watch,
|
||||
TEST_WATCH_SIGNAL id);
|
||||
|
||||
void
|
||||
test_watch_emit_queued_signals(
|
||||
OfonoWatch* watch);
|
||||
|
||||
void
|
||||
test_watch_set_ofono_sim(
|
||||
OfonoWatch* watch,
|
||||
struct ofono_sim* sim);
|
||||
|
||||
void
|
||||
test_watch_set_ofono_iccid(
|
||||
OfonoWatch* watch,
|
||||
const char* iccid);
|
||||
|
||||
void
|
||||
test_watch_set_ofono_imsi(
|
||||
OfonoWatch* watch,
|
||||
const char* imsi);
|
||||
|
||||
void
|
||||
test_watch_set_ofono_spn(
|
||||
OfonoWatch* watch,
|
||||
const char* spn);
|
||||
|
||||
void
|
||||
test_watch_set_ofono_netreg(
|
||||
OfonoWatch* watch,
|
||||
struct ofono_netreg* netreg);
|
||||
|
||||
#endif /* TEST_WATCH_H */
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
53
unit/coverage/run
Executable file
53
unit/coverage/run
Executable file
@@ -0,0 +1,53 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# This script requires lcov, dirname
|
||||
#
|
||||
|
||||
TESTS="\
|
||||
unit_base \
|
||||
unit_sim_settings"
|
||||
|
||||
function err() {
|
||||
echo "*** ERROR!" $1
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Check the required tools
|
||||
which lcov >> /dev/null || err "Please install lcov"
|
||||
which dirname >> /dev/null || err "Please install dirname"
|
||||
|
||||
# LCOV 1.10 has branch coverage disabled per default
|
||||
# Previous versions didn't have the --rc option
|
||||
if [ ! -z "$(lcov --help | grep ' --rc ')" ] ; then
|
||||
LCOV_OPT="--rc lcov_branch_coverage=1"
|
||||
GENHTML_OPT="--branch-coverage"
|
||||
fi
|
||||
|
||||
pushd `dirname $0` > /dev/null
|
||||
COV_DIR="$PWD"
|
||||
pushd .. > /dev/null
|
||||
TEST_DIR="$PWD"
|
||||
pushd .. > /dev/null
|
||||
TOP_DIR="$PWD"
|
||||
popd > /dev/null
|
||||
popd > /dev/null
|
||||
popd > /dev/null
|
||||
|
||||
make -C "$TOP_DIR" clean
|
||||
for t in $TESTS ; do
|
||||
pushd "$TEST_DIR/$t"
|
||||
make -C "$TEST_DIR/$t" clean coverage || exit 1
|
||||
build/coverage/$t || exit 1
|
||||
popd
|
||||
done
|
||||
|
||||
# Sometimes you need this, sometimes that :S
|
||||
BASE_DIR="$TOP_DIR"
|
||||
#BASE_DIR="$TOP_DIR/src"
|
||||
|
||||
FULL_COV="$COV_DIR/full.gcov"
|
||||
LIB_COV="$COV_DIR/lib.gcov"
|
||||
rm -f "$FULL_COV" "$LIB_COV"
|
||||
lcov $LCOV_OPT -c -d "$TOP_DIR/build/coverage" -b "$BASE_DIR" -o "$FULL_COV" || exit 1
|
||||
lcov $LCOV_OPT -e "$FULL_COV" "$BASE_DIR/*" -o "$LIB_COV" || exit 1
|
||||
genhtml $GENHTML_OPT "$LIB_COV" -t "ofono binder plugin" --output-directory "$COV_DIR/report" || exit 1
|
||||
5
unit/unit_base/Makefile
Normal file
5
unit/unit_base/Makefile
Normal file
@@ -0,0 +1,5 @@
|
||||
# -*- Mode: makefile-gmake -*-
|
||||
|
||||
EXE = unit_base
|
||||
|
||||
include ../common/Makefile
|
||||
156
unit/unit_base/unit_base.c
Normal file
156
unit/unit_base/unit_base.c
Normal file
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "binder_base.h"
|
||||
#include "binder_log.h"
|
||||
|
||||
#include <gutil_misc.h>
|
||||
#include <gutil_log.h>
|
||||
|
||||
GLOG_MODULE_DEFINE("unit_base");
|
||||
|
||||
/*==========================================================================*
|
||||
* Test object
|
||||
*==========================================================================*/
|
||||
|
||||
typedef enum test_property {
|
||||
TEST_PROPERTY_ANY,
|
||||
TEST_PROPERTY_ONE,
|
||||
TEST_PROPERTY_TWO,
|
||||
TEST_PROPERTY_COUNT
|
||||
} TEST_PROPERTY;
|
||||
|
||||
typedef BinderBaseClass TestObjectClass;
|
||||
typedef struct test_object_data {
|
||||
void* ptr;
|
||||
} TestObjectData;
|
||||
typedef struct test_object {
|
||||
BinderBase base;
|
||||
TestObjectData pub;
|
||||
} TestObject;
|
||||
|
||||
G_DEFINE_TYPE(TestObject, test_object, BINDER_TYPE_BASE)
|
||||
#define PARENT_CLASS test_object_parent_class
|
||||
#define TEST_TYPE test_object_get_type()
|
||||
#define TEST(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, TEST_TYPE, TestObject)
|
||||
BINDER_BASE_ASSERT_COUNT(TEST_PROPERTY_COUNT);
|
||||
|
||||
static
|
||||
void
|
||||
test_object_init(
|
||||
TestObject* self)
|
||||
{
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
test_object_class_init(
|
||||
TestObjectClass* klass)
|
||||
{
|
||||
BINDER_BASE_CLASS(klass)->public_offset = G_STRUCT_OFFSET(TestObject, pub);
|
||||
}
|
||||
|
||||
/*==========================================================================*
|
||||
* basic
|
||||
*==========================================================================*/
|
||||
|
||||
typedef struct test_basic_data {
|
||||
int total;
|
||||
int count[TEST_PROPERTY_COUNT];
|
||||
} TestBasicData;
|
||||
|
||||
static
|
||||
void
|
||||
test_basic_cb(
|
||||
TestObjectData* data,
|
||||
TEST_PROPERTY property,
|
||||
void* user_data)
|
||||
{
|
||||
TestBasicData* test = user_data;
|
||||
|
||||
g_assert(data->ptr == user_data);
|
||||
g_assert_cmpint(property, > ,TEST_PROPERTY_ANY);
|
||||
g_assert_cmpint(property, < ,TEST_PROPERTY_COUNT);
|
||||
test->count[property]++;
|
||||
test->total++;
|
||||
GDEBUG("%d %d", property, test->total);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
test_basic(
|
||||
void)
|
||||
{
|
||||
TestBasicData test;
|
||||
TestObject* obj = g_object_new(TEST_TYPE, NULL);
|
||||
BinderBase* base = &obj->base;
|
||||
ulong id;
|
||||
|
||||
obj->pub.ptr = &test;
|
||||
memset(&test, 0, sizeof(test));
|
||||
id = binder_base_add_property_handler(base, TEST_PROPERTY_ANY,
|
||||
G_CALLBACK(test_basic_cb), &test);
|
||||
g_assert(id);
|
||||
|
||||
/* NULL callback is tolerated */
|
||||
g_assert(!binder_base_add_property_handler(base, TEST_PROPERTY_ANY,
|
||||
NULL, NULL));
|
||||
|
||||
/* Queue two events. Not signals is emitted yet */
|
||||
binder_base_queue_property_change(base, TEST_PROPERTY_ONE);
|
||||
binder_base_queue_property_change(base, TEST_PROPERTY_TWO);
|
||||
g_assert_cmpint(test.total, == ,0);
|
||||
|
||||
/* Emit queued signals */
|
||||
binder_base_emit_queued_signals(base);
|
||||
g_assert_cmpint(test.total, == ,2);
|
||||
g_assert_cmpint(test.count[TEST_PROPERTY_ONE], == ,1);
|
||||
g_assert_cmpint(test.count[TEST_PROPERTY_TWO], == ,1);
|
||||
|
||||
/* And emit one more */
|
||||
binder_base_emit_property_change(base, TEST_PROPERTY_ONE);
|
||||
g_assert_cmpint(test.total, == ,3);
|
||||
g_assert_cmpint(test.count[TEST_PROPERTY_ONE], == ,2);
|
||||
|
||||
g_signal_handler_disconnect(base, id);
|
||||
g_object_unref(obj);
|
||||
}
|
||||
|
||||
/*==========================================================================*
|
||||
* Common
|
||||
*==========================================================================*/
|
||||
|
||||
#define TEST_PREFIX "/base/"
|
||||
#define TEST_(t) TEST_PREFIX t
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
g_test_add_func(TEST_("basic"), test_basic);
|
||||
|
||||
gutil_log_default.level = g_test_verbose() ?
|
||||
GLOG_LEVEL_VERBOSE : GLOG_LEVEL_NONE;
|
||||
gutil_log_timestamp = FALSE;
|
||||
|
||||
return g_test_run();
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
7
unit/unit_sim_settings/Makefile
Normal file
7
unit/unit_sim_settings/Makefile
Normal file
@@ -0,0 +1,7 @@
|
||||
# -*- Mode: makefile-gmake -*-
|
||||
|
||||
COMMON_SRC += test_watch.c
|
||||
|
||||
EXE = unit_sim_settings
|
||||
|
||||
include ../common/Makefile
|
||||
169
unit/unit_sim_settings/unit_sim_settings.c
Normal file
169
unit/unit_sim_settings/unit_sim_settings.c
Normal file
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* oFono - Open Source Telephony - binder based adaptation
|
||||
*
|
||||
* Copyright (C) 2021 Jolla Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include "test_watch.h"
|
||||
|
||||
#include "binder_sim_settings.h"
|
||||
#include "binder_log.h"
|
||||
|
||||
#include <gutil_misc.h>
|
||||
#include <gutil_log.h>
|
||||
|
||||
GLOG_MODULE_DEFINE("unit_sim_settings");
|
||||
|
||||
/*==========================================================================*
|
||||
* null
|
||||
*==========================================================================*/
|
||||
static
|
||||
void
|
||||
test_null(
|
||||
void)
|
||||
{
|
||||
g_assert(!binder_sim_settings_new(NULL, OFONO_RADIO_ACCESS_MODE_NONE));
|
||||
g_assert(!binder_sim_settings_add_property_handler(NULL, 0, NULL, NULL));
|
||||
g_assert(!binder_sim_settings_ref(NULL));
|
||||
binder_sim_settings_unref(NULL);
|
||||
binder_sim_settings_set_pref(NULL, OFONO_RADIO_ACCESS_MODE_NONE);
|
||||
binder_sim_settings_remove_handler(NULL, 0);
|
||||
binder_sim_settings_remove_handlers(NULL, NULL, 0);
|
||||
}
|
||||
|
||||
/*==========================================================================*
|
||||
* basic
|
||||
*==========================================================================*/
|
||||
|
||||
typedef struct test_basic_data {
|
||||
int count[BINDER_SIM_SETTINGS_PROPERTY_COUNT];
|
||||
} TestBasicData;
|
||||
|
||||
static
|
||||
void
|
||||
test_basic_total_cb(
|
||||
BinderSimSettings* data,
|
||||
BINDER_SIM_SETTINGS_PROPERTY property,
|
||||
void* user_data)
|
||||
{
|
||||
TestBasicData* test = user_data;
|
||||
|
||||
test->count[BINDER_SIM_SETTINGS_PROPERTY_ANY]++;
|
||||
GDEBUG("%d %d", property, test->count[BINDER_SIM_SETTINGS_PROPERTY_ANY]);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
test_basic_property_cb(
|
||||
BinderSimSettings* data,
|
||||
BINDER_SIM_SETTINGS_PROPERTY property,
|
||||
void* user_data)
|
||||
{
|
||||
TestBasicData* test = user_data;
|
||||
|
||||
g_assert_cmpint(property, < ,BINDER_SIM_SETTINGS_PROPERTY_COUNT);
|
||||
g_assert_cmpint(property, > ,BINDER_SIM_SETTINGS_PROPERTY_ANY);
|
||||
test->count[property]++;
|
||||
GDEBUG("%d %d", property, test->count[property]);
|
||||
}
|
||||
|
||||
static
|
||||
void
|
||||
test_basic(
|
||||
void)
|
||||
{
|
||||
TestBasicData test;
|
||||
const char* path = "/test_0";
|
||||
const char* iccid = "8888888888888888888";
|
||||
const char* imsi = "222222222222222";
|
||||
enum ofono_radio_access_mode pref = OFONO_RADIO_ACCESS_MODE_GSM;
|
||||
OfonoWatch* watch = ofono_watch_new(path);
|
||||
BinderSimSettings* settings = binder_sim_settings_new(path,
|
||||
OFONO_RADIO_ACCESS_MODE_ALL);
|
||||
ulong id[BINDER_SIM_SETTINGS_PROPERTY_COUNT];
|
||||
guint i;
|
||||
|
||||
memset(&test, 0, sizeof(test));
|
||||
id[0] = binder_sim_settings_add_property_handler(settings,
|
||||
BINDER_SIM_SETTINGS_PROPERTY_ANY, test_basic_total_cb, &test);
|
||||
g_assert(id[0]);
|
||||
for (i = 1; i < G_N_ELEMENTS(id); i++) {
|
||||
id[i] = binder_sim_settings_add_property_handler(settings, i,
|
||||
test_basic_property_cb, &test);
|
||||
g_assert(id[i]);
|
||||
}
|
||||
|
||||
/* NULL callback and zero id are tolerated */
|
||||
binder_sim_settings_remove_handler(settings, 0);
|
||||
g_assert(!binder_sim_settings_add_property_handler(settings,
|
||||
BINDER_SIM_SETTINGS_PROPERTY_ANY, NULL, NULL));
|
||||
|
||||
/* Change some properties */
|
||||
test_watch_set_ofono_iccid(watch, iccid); /* Ignored */
|
||||
test_watch_emit_queued_signals(watch);
|
||||
g_assert_cmpint(test.count[BINDER_SIM_SETTINGS_PROPERTY_ANY], == ,0);
|
||||
|
||||
test_watch_set_ofono_imsi(watch, imsi); /* Handled */
|
||||
test_watch_emit_queued_signals(watch);
|
||||
g_assert_cmpstr(settings->imsi, == ,imsi);
|
||||
g_assert_cmpint(test.count[BINDER_SIM_SETTINGS_PROPERTY_ANY], == ,1);
|
||||
g_assert_cmpint(test.count[BINDER_SIM_SETTINGS_PROPERTY_IMSI], == ,1);
|
||||
|
||||
/* No signal is emitted if IMSI hasn't actually changed */
|
||||
test_watch_signal_queue(watch, TEST_WATCH_SIGNAL_IMSI_CHANGED);
|
||||
test_watch_emit_queued_signals(watch);
|
||||
g_assert_cmpint(test.count[BINDER_SIM_SETTINGS_PROPERTY_ANY], == ,1);
|
||||
g_assert_cmpint(test.count[BINDER_SIM_SETTINGS_PROPERTY_IMSI], == ,1);
|
||||
|
||||
/* Second time doesn't emit anything since the value doesn't change */
|
||||
binder_sim_settings_set_pref(settings, pref);
|
||||
binder_sim_settings_set_pref(settings, pref);
|
||||
g_assert_cmpint(settings->pref, == ,pref);
|
||||
g_assert_cmpint(test.count[BINDER_SIM_SETTINGS_PROPERTY_ANY], == ,2);
|
||||
g_assert_cmpint(test.count[BINDER_SIM_SETTINGS_PROPERTY_PREF], == ,1);
|
||||
|
||||
binder_sim_settings_remove_handler(settings, id[0]);
|
||||
id[0] = 0; /* binder_sim_settings_remove_handlers ignores zeros */
|
||||
binder_sim_settings_remove_all_handlers(settings, id);
|
||||
g_assert(binder_sim_settings_ref(settings) == settings);
|
||||
binder_sim_settings_unref(settings);
|
||||
binder_sim_settings_unref(settings);
|
||||
ofono_watch_unref(watch);
|
||||
}
|
||||
|
||||
/*==========================================================================*
|
||||
* Common
|
||||
*==========================================================================*/
|
||||
|
||||
#define TEST_PREFIX "/sim_settings/"
|
||||
#define TEST_(t) TEST_PREFIX t
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
g_test_add_func(TEST_("null"), test_null);
|
||||
g_test_add_func(TEST_("basic"), test_basic);
|
||||
|
||||
gutil_log_default.level = g_test_verbose() ?
|
||||
GLOG_LEVEL_VERBOSE : GLOG_LEVEL_NONE;
|
||||
gutil_log_timestamp = FALSE;
|
||||
|
||||
return g_test_run();
|
||||
}
|
||||
|
||||
/*
|
||||
* Local Variables:
|
||||
* mode: C
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
Reference in New Issue
Block a user