diff --git a/Makefile b/Makefile index 2266818..94c0aed 100644 --- a/Makefile +++ b/Makefile @@ -52,6 +52,7 @@ SRC = \ binder_gprs.c \ binder_gprs_context.c \ binder_ims.c \ + binder_ims_reg.c \ binder_logger.c \ binder_modem.c \ binder_netreg.c \ diff --git a/src/binder_ims.c b/src/binder_ims.c index 89b41c1..aca0f83 100644 --- a/src/binder_ims.c +++ b/src/binder_ims.c @@ -14,30 +14,23 @@ */ #include "binder_ims.h" +#include "binder_ims_reg.h" #include "binder_log.h" #include "binder_modem.h" #include "binder_util.h" #include -#include -#include -#include - -#include - -enum binder_sms_events { - IMS_EVENT_IMS_NETWORK_STATE_CHANGED, - IMS_EVENT_COUNT +enum binder_ims_events { + EVENT_IMS_REGISTRATION_CHANGED, + EVENT_COUNT }; typedef struct binder_ims { struct ofono_ims* ims; - gboolean registered; - int flags; char* log_prefix; - RadioRequestGroup* g; - gulong event_id[IMS_EVENT_COUNT]; + BinderImsReg* reg; + gulong event_id[EVENT_COUNT]; guint start_id; } BinderIms; @@ -45,76 +38,23 @@ typedef struct binder_ims { static inline BinderIms* binder_ims_get_data(struct ofono_ims* ims) { return ofono_ims_get_data(ims); } +static gboolean binder_ims_registered(BinderIms* self) + { return self->reg && self->reg->registered; } +static int binder_ims_flags(BinderIms* self) + { return binder_ims_registered(self) ? OFONO_IMS_SMS_CAPABLE : 0; } static void -binder_ims_query_registration_state_cb( - RadioRequest* req, - RADIO_TX_STATUS status, - RADIO_RESP resp, - RADIO_ERROR error, - const GBinderReader* args, - gpointer user_data) -{ - BinderIms* self = user_data; - - if (status == RADIO_TX_STATUS_OK) { - if (resp == RADIO_RESP_GET_IMS_REGISTRATION_STATE) { - if (error == RADIO_ERROR_NONE) { - GBinderReader reader; - gboolean registered; - gint32 rat; - - /* - * getImsRegistrationStateResponse(RadioResponseInfo info, - * bool isRegistered, RadioTechnologyFamily ratFamily) - */ - gbinder_reader_copy(&reader, args); - if (gbinder_reader_read_bool(&reader, ®istered) && - gbinder_reader_read_int32(&reader, &rat)) { - DBG_(self, "registered: %d, rat: 0x%08x", registered, rat); - if (self->registered != registered) { - self->registered = registered; - self->flags = registered ? OFONO_IMS_SMS_CAPABLE : 0; - ofono_ims_status_notify(self->ims, registered, - self->flags); - } - } - } else { - DBG_(self, "%s", binder_radio_error_string(error)); - } - } else { - ofono_error("Unexpected getImsRegistrationState response %d", - resp); - } - } -} - -static -void -binder_ims_query_registration_state( - BinderIms* self) -{ - RadioRequest* req = radio_request_new2(self->g, - RADIO_REQ_GET_IMS_REGISTRATION_STATE, NULL, - binder_ims_query_registration_state_cb, NULL, self); - - radio_request_submit(req); - radio_request_unref(req); -} - -static -void -binder_ims_network_state_changed( - RadioClient* client, - RADIO_IND code, - const GBinderReader* args, +binder_ims_registration_changed( + BinderImsReg* reg, + BINDER_IMS_REG_PROPERTY property, gpointer user_data) { BinderIms* self = user_data; DBG_(self, ""); - binder_ims_query_registration_state(self); + ofono_ims_status_notify(self->ims, binder_ims_registered(self), + binder_ims_flags(self)); } static @@ -127,7 +67,8 @@ binder_ims_registration_status( BinderIms* self = binder_ims_get_data(ims); struct ofono_error err; - cb(binder_error_ok(&err), self->registered, self->flags, data); + cb(binder_error_ok(&err), binder_ims_registered(self), + binder_ims_flags(self), data); } static @@ -136,22 +77,17 @@ binder_ims_start( gpointer user_data) { BinderIms* self = user_data; - RadioClient* client = self->g->client; DBG_(self, ""); GASSERT(self->start_id); self->start_id = 0; + self->event_id[EVENT_IMS_REGISTRATION_CHANGED] = + binder_ims_reg_add_property_handler(self->reg, + BINDER_IMS_REG_PROPERTY_REGISTERED, + binder_ims_registration_changed, self); + ofono_ims_register(self->ims); - - /* Register event handlers */ - self->event_id[IMS_EVENT_IMS_NETWORK_STATE_CHANGED] = - radio_client_add_indication_handler(client, - RADIO_IND_IMS_NETWORK_STATE_CHANGED, - binder_ims_network_state_changed, self); - - /* Request the initial state */ - binder_ims_query_registration_state(self); return G_SOURCE_REMOVE; } @@ -167,7 +103,7 @@ binder_ims_probe( self->log_prefix = binder_dup_prefix(modem->log_prefix); DBG_(self, ""); - self->g = radio_request_group_new(modem->client); /* Keeps ref to client */ + self->reg = binder_ims_reg_ref(modem->ims); self->ims = ims; self->start_id = g_idle_add(binder_ims_start, self); @@ -188,9 +124,8 @@ binder_ims_remove( g_source_remove(self->start_id); } - radio_client_remove_all_handlers(self->g->client, self->event_id); - radio_request_group_cancel(self->g); - radio_request_group_unref(self->g); + binder_ims_reg_remove_all_handlers(self->reg, self->event_id); + binder_ims_reg_unref(self->reg); g_free(self->log_prefix); g_free(self); diff --git a/src/binder_ims_reg.c b/src/binder_ims_reg.c new file mode 100644 index 0000000..82e97ec --- /dev/null +++ b/src/binder_ims_reg.c @@ -0,0 +1,275 @@ +/* + * oFono - Open Source Telephony - binder based adaptation + * + * Copyright (C) 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_ims_reg.h" +#include "binder_log.h" +#include "binder_util.h" + +#include +#include +#include + +#include +#include +#include + +enum binder_ims_events { + EVENT_IMS_NETWORK_STATE_CHANGED, + EVENT_COUNT +}; + +typedef struct binder_ims_reg_object { + BinderBase base; + BinderImsReg pub; + RadioRequestGroup* g; + char* log_prefix; + gulong event_id[EVENT_COUNT]; +} BinderImsRegObject; + +typedef BinderBaseClass BinderImsRegObjectClass; +GType binder_ims_reg_object_get_type() BINDER_INTERNAL; +G_DEFINE_TYPE(BinderImsRegObject, binder_ims_reg_object, BINDER_TYPE_BASE) +#define PARENT_CLASS binder_ims_reg_object_parent_class +#define THIS_TYPE binder_ims_reg_object_get_type() +#define THIS(obj) G_TYPE_CHECK_INSTANCE_CAST(obj,THIS_TYPE,BinderImsRegObject) + +#define DBG_(self,fmt,args...) DBG("%s" fmt, (self)->log_prefix, ##args) + +/* Assumptions */ +BINDER_BASE_ASSERT_COUNT(BINDER_IMS_REG_PROPERTY_COUNT); + +static inline BinderImsRegObject* binder_ims_reg_cast(BinderImsReg* ims) + { return ims ? THIS(G_CAST(ims, BinderImsRegObject, pub)) : NULL; } +static inline void binder_ims_reg_object_ref(BinderImsRegObject* self) + { g_object_ref(self); } +static inline void binder_ims_reg_object_unref(BinderImsRegObject* self) + { g_object_unref(self); } + +static +void +binder_ims_reg_query_done( + RadioRequest* req, + RADIO_TX_STATUS status, + RADIO_RESP resp, + RADIO_ERROR error, + const GBinderReader* args, + gpointer user_data) +{ + BinderImsRegObject* self = THIS(user_data); + BinderBase* base = &self->base; + BinderImsReg* ims = &self->pub; + gboolean registered = FALSE; + gint32 rat = RADIO_TECH_FAMILY_3GPP; + + if (status != RADIO_TX_STATUS_OK) { + ofono_error("getImsRegistrationState failed"); + } else if (resp != RADIO_RESP_GET_IMS_REGISTRATION_STATE) { + ofono_error("Unexpected getImsRegistrationState response %d", resp); + } else if (error != RADIO_ERROR_NONE) { + DBG_(self, "%s", binder_radio_error_string(error)); + } else { + GBinderReader reader; + + /* + * getImsRegistrationStateResponse(RadioResponseInfo info, + * bool isRegistered, RadioTechnologyFamily ratFamily) + */ + gbinder_reader_copy(&reader, args); + if (gbinder_reader_read_bool(&reader, ®istered) && + gbinder_reader_read_int32(&reader, &rat)) { + DBG_(self, "registered: %d, rat: %d", registered, rat); + } else { + ofono_error("Failed to parse getImsRegistrationState response"); + } + } + + /* Any error is treated as an unregistered state */ + if (ims->registered != registered) { + ims->registered = registered; + binder_base_queue_property_change(base, + BINDER_IMS_REG_PROPERTY_REGISTERED); + } + if (ims->tech_family != rat) { + ims->tech_family = rat; + binder_base_queue_property_change(base, + BINDER_IMS_REG_PROPERTY_TECH_FAMILY); + } + binder_base_emit_queued_signals(base); +} + +static +void +binder_ims_reg_query( + BinderImsRegObject* self) +{ + RadioRequest* req = radio_request_new2(self->g, + RADIO_REQ_GET_IMS_REGISTRATION_STATE, NULL, + binder_ims_reg_query_done, NULL, self); + + radio_request_submit(req); + radio_request_unref(req); +} + +static +void +binder_ims_reg_state_changed( + RadioClient* client, + RADIO_IND code, + const GBinderReader* args, + gpointer user_data) +{ + BinderImsRegObject* self = THIS(user_data); + + DBG_(self, ""); + binder_ims_reg_query(self); +} + +/*==========================================================================* + * API + *==========================================================================*/ + +BinderImsReg* +binder_ims_reg_new( + RadioClient* client, + const char* log_prefix) +{ + BinderImsReg* ims = NULL; + + if (client) { + BinderImsRegObject* self = g_object_new(THIS_TYPE, NULL); + + ims = &self->pub; + self->log_prefix = binder_dup_prefix(log_prefix); + self->g = radio_request_group_new(client); /* Keeps ref to client */ + DBG_(self, ""); + + /* Register event handlers */ + self->event_id[EVENT_IMS_NETWORK_STATE_CHANGED] = + radio_client_add_indication_handler(client, + RADIO_IND_IMS_NETWORK_STATE_CHANGED, + binder_ims_reg_state_changed, self); + + /* Query the initial state */ + binder_ims_reg_query(self); + } + return ims; +} + +BinderImsReg* +binder_ims_reg_ref( + BinderImsReg* ims) +{ + BinderImsRegObject* self = binder_ims_reg_cast(ims); + + if (G_LIKELY(self)) { + binder_ims_reg_object_ref(self); + return ims; + } else { + return NULL; + } +} + +void +binder_ims_reg_unref( + BinderImsReg* ims) +{ + BinderImsRegObject* self = binder_ims_reg_cast(ims); + + if (G_LIKELY(self)) { + binder_ims_reg_object_unref(self); + } +} + +gulong +binder_ims_reg_add_property_handler( + BinderImsReg* ims, + BINDER_IMS_REG_PROPERTY property, + BinderImsRegPropertyFunc callback, + void* user_data) +{ + BinderImsRegObject* self = binder_ims_reg_cast(ims); + + return G_LIKELY(self) ? binder_base_add_property_handler(&self->base, + property, G_CALLBACK(callback), user_data) : 0; +} + +void +binder_ims_reg_remove_handler( + BinderImsReg* ims, + gulong id) +{ + if (G_LIKELY(id)) { + BinderImsRegObject* self = binder_ims_reg_cast(ims); + + if (G_LIKELY(self)) { + g_signal_handler_disconnect(self, id); + } + } +} + +void +binder_ims_reg_remove_handlers( + BinderImsReg* ims, + gulong* ids, + int count) +{ + gutil_disconnect_handlers(binder_ims_reg_cast(ims), ids, count); +} + +/*==========================================================================* + * Internals + *==========================================================================*/ + +static +void +binder_ims_reg_object_init( + BinderImsRegObject* self) +{ +} + +static +void +binder_ims_reg_object_finalize( + GObject* object) +{ + BinderImsRegObject* self = THIS(object); + RadioRequestGroup* g = self->g; + + radio_client_remove_all_handlers(g->client, self->event_id); + radio_request_group_cancel(g); + radio_request_group_unref(g); + g_free(self->log_prefix); + + G_OBJECT_CLASS(PARENT_CLASS)->finalize(object); +} + +static +void +binder_ims_reg_object_class_init( + BinderImsRegObjectClass* klass) +{ + G_OBJECT_CLASS(klass)->finalize = binder_ims_reg_object_finalize; + BINDER_BASE_CLASS(klass)->public_offset = + G_STRUCT_OFFSET(BinderImsRegObject, pub); +} + +/* + * Local Variables: + * mode: C + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/src/binder_ims_reg.h b/src/binder_ims_reg.h new file mode 100644 index 0000000..3a4c747 --- /dev/null +++ b/src/binder_ims_reg.h @@ -0,0 +1,89 @@ +/* + * oFono - Open Source Telephony - binder based adaptation + * + * Copyright (C) 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_IMS_REG_H +#define BINDER_IMS_REG_H + +#include "binder_types.h" + +/* Object tracking IMS registration state */ + +typedef enum binder_ims_reg_property { + BINDER_IMS_REG_PROPERTY_ANY, + BINDER_IMS_REG_PROPERTY_REGISTERED, + BINDER_IMS_REG_PROPERTY_TECH_FAMILY, + BINDER_IMS_REG_PROPERTY_COUNT +} BINDER_IMS_REG_PROPERTY; + +struct binder_ims_reg { + gboolean registered; + RADIO_TECH_FAMILY tech_family; +}; + +typedef +void +(*BinderImsRegPropertyFunc)( + BinderImsReg* ims, + BINDER_IMS_REG_PROPERTY property, + void* user_data); + +BinderImsReg* +binder_ims_reg_new( + RadioClient* client, + const char* log_prefix); + +BinderImsReg* +binder_ims_reg_ref( + BinderImsReg* ims) + BINDER_INTERNAL; + +void +binder_ims_reg_unref( + BinderImsReg* ims) + BINDER_INTERNAL; + +gulong +binder_ims_reg_add_property_handler( + BinderImsReg* ims, + BINDER_IMS_REG_PROPERTY property, + BinderImsRegPropertyFunc callback, + void* user_data) + BINDER_INTERNAL; + +void +binder_ims_reg_remove_handler( + BinderImsReg* ims, + gulong id) + BINDER_INTERNAL; + +void +binder_ims_reg_remove_handlers( + BinderImsReg* ims, + gulong* ids, + int count) + BINDER_INTERNAL; + +#define binder_ims_reg_remove_all_handlers(ims, ids) \ + binder_ims_reg_remove_handlers(ims, ids, G_N_ELEMENTS(ids)) + +#endif /* BINDER_IMS_REG_H */ + +/* + * Local Variables: + * mode: C + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/src/binder_modem.c b/src/binder_modem.c index 2226fd9..d19353e 100644 --- a/src/binder_modem.c +++ b/src/binder_modem.c @@ -19,6 +19,7 @@ #include "binder_sim_card.h" #include "binder_sim_settings.h" #include "binder_cell_info.h" +#include "binder_ims_reg.h" #include "binder_data.h" #include "binder_util.h" #include "binder_log.h" @@ -487,6 +488,7 @@ binder_modem_remove( g_source_remove(self->set_offline.timeout_id); } + binder_ims_reg_unref(modem->ims); binder_network_unref(modem->network); binder_sim_card_unref(modem->sim_card); binder_data_unref(modem->data); @@ -575,6 +577,7 @@ binder_modem_create( modem->data = binder_data_ref(data); modem->watch = ofono_watch_new(path); modem->client = radio_client_ref(client); + modem->ims = binder_ims_reg_new(client, log_prefix); self->g = radio_request_group_new(client); self->last_known_iccid = g_strdup(modem->watch->iccid); diff --git a/src/binder_modem.h b/src/binder_modem.h index 334ce55..dfb0c96 100644 --- a/src/binder_modem.h +++ b/src/binder_modem.h @@ -30,6 +30,7 @@ struct binder_modem { struct ofono_cell_info* cell_info; struct ofono_watch* watch; BinderData* data; + BinderImsReg* ims; BinderNetwork* network; BinderRadio* radio; BinderSimCard* sim_card; diff --git a/src/binder_types.h b/src/binder_types.h index 68b33e6..8bc4d9d 100644 --- a/src/binder_types.h +++ b/src/binder_types.h @@ -23,6 +23,7 @@ typedef struct binder_data BinderData; typedef struct binder_data_manager BinderDataManager; typedef struct binder_devmon BinderDevmon; +typedef struct binder_ims_reg BinderImsReg; typedef struct binder_logger BinderLogger; typedef struct binder_modem BinderModem; typedef struct binder_network BinderNetwork;