[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