From 68ca473945ac2070b6cca941943e1b07c71e78bf Mon Sep 17 00:00:00 2001 From: Marius Gripsgard Date: Tue, 8 Oct 2024 02:19:12 +0200 Subject: [PATCH] Checkpoint: inital impl of qti radio This now registrers and reports the state to ofono --- Makefile | 15 +- src/qti_ims.c | 136 ++++++- src/qti_ims.h | 5 +- src/qti_radio_ext.c | 720 ++++++++++++++++++++++++++++++++++++++ src/qti_radio_ext.h | 73 ++++ src/qti_radio_ext_types.h | 200 +++++++++++ src/qti_slot.c | 9 +- 7 files changed, 1140 insertions(+), 18 deletions(-) create mode 100644 src/qti_radio_ext.c create mode 100644 src/qti_radio_ext.h create mode 100644 src/qti_radio_ext_types.h diff --git a/Makefile b/Makefile index ac97fae..c9eebef 100644 --- a/Makefile +++ b/Makefile @@ -2,15 +2,18 @@ .PHONY: clean all debug release +# Allow building against an oFono variant. +OFONO_PKG ?= ofono + # # Required packages # -# ofono.pc adds -export-symbols-regex linker option which doesn't work +# ofono.pc adds -export-symbols-regex linker option which doesn't work # on all platforms. # LDPKGS = libofonobinderpluginext libgbinder-radio libgbinder libglibutil gobject-2.0 glib-2.0 -PKGS = ofono $(LDPKGS) +PKGS = $(OFONO_PKG) $(LDPKGS) # # Default target @@ -36,7 +39,8 @@ SRC = \ qti_ext.c \ qti_ims.c \ qti_plugin.c \ - qti_slot.c + qti_slot.c \ + qti_radio_ext.c # # Directories @@ -125,15 +129,12 @@ $(DEBUG_SO): $(DEBUG_OBJS) $(RELEASE_SO): $(RELEASE_OBJS) $(LD) $(RELEASE_OBJS) $(RELEASE_LDFLAGS) $(RELEASE_LIBS) -o $@ -ifeq ($(KEEP_SYMBOLS),0) - $(STRIP) $@ -endif # # Install # -PLUGINDIR ?= $$(pkg-config ofono --variable=plugindir) +PLUGINDIR ?= $$(pkg-config $(OFONO_PKG) --variable=plugindir) ABS_PLUGINDIR := $(shell echo /$(PLUGINDIR) | sed -r 's|/+|/|g') INSTALL = install diff --git a/src/qti_ims.c b/src/qti_ims.c index 13cf926..b25655d 100644 --- a/src/qti_ims.c +++ b/src/qti_ims.c @@ -37,15 +37,25 @@ #include "qti_ims.h" #include "qti_slot.h" +#include "qti_radio_ext.h" #include #include +#include +#include +#include + +#define DBG(fmt, ...) \ + gutil_log(GLOG_MODULE_CURRENT, GLOG_LEVEL_ALWAYS, fmt, ##__VA_ARGS__) + typedef GObjectClass QtiImsClass; typedef struct qti_ims { GObject parent; char* slot; + QtiRadioExt* radio_ext; + BINDER_EXT_IMS_STATE ims_state; } QtiIms; static @@ -70,6 +80,95 @@ enum qti_ims_signal { static guint qti_ims_signals[SIGNAL_COUNT] = { 0 }; +typedef struct qti_ims_result_request { + BinderExtIms* ext; + BinderExtImsResultFunc complete; + GDestroyNotify destroy; + void* user_data; +} QtiImsResultRequest; + +static +QtiImsResultRequest* +qti_ims_result_request_new( + BinderExtIms* ext, + BinderExtImsResultFunc complete, + GDestroyNotify destroy, + void* user_data) +{ + QtiImsResultRequest* req = g_slice_new(QtiImsResultRequest); + + req->ext = binder_ext_ims_ref(ext); + req->complete = complete; + req->destroy = destroy; + req->user_data = user_data; + return req; +} + +static +void +qti_ims_result_request_free( + QtiImsResultRequest* req) +{ + binder_ext_ims_unref(req->ext); + gutil_slice_free(req); +} + +static +void +qti_ims_result_request_complete( + QtiRadioExt* radio_ext, + int result, + void* user_data) +{ + QtiImsResultRequest* req = user_data; + + req->complete(req->ext, result ? BINDER_EXT_IMS_RESULT_ERROR : + BINDER_EXT_IMS_RESULT_OK, req->user_data); +} + +static +void +qti_ims_result_request_destroy( + gpointer user_data) +{ + QtiImsResultRequest* req = user_data; + + if (req->destroy) { + req->destroy(req->user_data); + } + qti_ims_result_request_free(req); +} + +static +void +qti_ims_reg_status_changed( + QtiRadioExt* radio, + QTI_RADIO_REG_STATE state, + void* user_data) +{ + QtiIms* self = THIS(user_data); + BINDER_EXT_IMS_STATE ims_state; + + switch (state) { + case QTI_RADIO_REG_STATE_REGISTERING: + ims_state = BINDER_EXT_IMS_STATE_REGISTERING; + break; + case QTI_RADIO_REG_STATE_REGISTERED: + ims_state = BINDER_EXT_IMS_STATE_REGISTERED; + break; + case QTI_RADIO_REG_STATE_NOT_REGISTERED: + ims_state = BINDER_EXT_IMS_STATE_NOT_REGISTERED; + break; + default: + ims_state = BINDER_EXT_IMS_STATE_UNKNOWN; + } + + if (ims_state != self->ims_state) { + self->ims_state = ims_state; + g_signal_emit(self, qti_ims_signals[SIGNAL_STATE_CHANGED], 0); + } +} + /*==========================================================================* * BinderExtImsInterface *==========================================================================*/ @@ -81,11 +180,12 @@ qti_ims_get_state( { QtiIms* self = THIS(ext); - DBG("%s", self->slot); -#pragma message("TODO: return the actual state") - return BINDER_EXT_IMS_STATE_UNKNOWN; + DBG("%s ims_state=%d", self->slot, self->ims_state); + return self->ims_state; } +// trun BINDER_EXT_IMS_REGISTRATION to QTI + static guint qti_ims_set_registration( @@ -96,14 +196,22 @@ qti_ims_set_registration( void* user_data) { QtiIms* self = THIS(ext); - const gboolean on = (registration != BINDER_EXT_IMS_REGISTRATION_OFF); + const gboolean enabled = (registration != BINDER_EXT_IMS_REGISTRATION_OFF); - DBG("%s %s", self->slot, on ? "on" : "off"); - if (on) { -#pragma message("TODO: turn IMS registration on") + QtiImsResultRequest* req = qti_ims_result_request_new(ext, + complete, destroy, user_data); + guint id = qti_radio_ext_set_reg_state(self->radio_ext, + registration, + complete ? qti_ims_result_request_complete : NULL, + qti_ims_result_request_destroy, req); + + DBG("%s %s", self->slot, enabled ? "on" : "off"); + if (enabled) { + return id; } else { -#pragma message("TODO: turn IMS registration off") + qti_ims_result_request_free(req); } + return 0; } @@ -154,7 +262,8 @@ qti_ims_iface_init( BinderExtIms* qti_ims_new( - const char* slot) + const char* slot, + QtiRadioExt* radio_ext) { QtiIms* self = g_object_new(THIS_TYPE, NULL); @@ -163,6 +272,14 @@ qti_ims_new( * on registration state change and emits SIGNAL_STATE_CHANGED. */ self->slot = g_strdup(slot); + self->radio_ext = qti_radio_ext_ref(radio_ext); + self->ims_state = BINDER_EXT_IMS_STATE_NOT_REGISTERED; + + if (self->radio_ext) { + qti_radio_ext_add_ims_reg_status_handler(self->radio_ext, + qti_ims_reg_status_changed, self); + } + return BINDER_EXT_IMS(self); } @@ -178,6 +295,7 @@ qti_ims_finalize( QtiIms* self = THIS(object); g_free(self->slot); + qti_radio_ext_unref(self->radio_ext); G_OBJECT_CLASS(PARENT_CLASS)->finalize(object); } diff --git a/src/qti_ims.h b/src/qti_ims.h index 3422581..fd70827 100644 --- a/src/qti_ims.h +++ b/src/qti_ims.h @@ -40,9 +40,12 @@ #include +typedef struct qti_radio_ext QtiRadioExt; + BinderExtIms* qti_ims_new( - const char* slot) + const char* slot, + QtiRadioExt* radio_ext) G_GNUC_INTERNAL; #endif /* QTI_IMS_H */ diff --git a/src/qti_radio_ext.c b/src/qti_radio_ext.c new file mode 100644 index 0000000..3dc9959 --- /dev/null +++ b/src/qti_radio_ext.c @@ -0,0 +1,720 @@ +/* + * oFono - Open Source Telephony - binder based adaptation QTI plugin + * + * Copyright (C) 2022 Jolla Ltd. + * Copyright (C) 2024 TheKit + * + * 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 + +#include "qti_radio_ext.h" +#include "qti_radio_ext_types.h" + +#include +#include + +#include +#include + +#include +#include +#include + +#define DBG(fmt, ...) \ + gutil_log(GLOG_MODULE_CURRENT, GLOG_LEVEL_ALWAYS, fmt, ##__VA_ARGS__) + + +#define QTI_RADIO_CALL_TIMEOUT (3*1000) /* ms */ + +typedef GObjectClass QtiRadioExtClass; +typedef struct qti_radio_ext { + GObject parent; + char* slot; + GBinderClient* client; + GBinderRemoteObject* remote; + GBinderLocalObject* response; + GBinderLocalObject* indication; + GUtilIdlePool* pool; + GHashTable* requests; +} QtiRadioExt; + +GType qti_radio_ext_get_type() G_GNUC_INTERNAL; +G_DEFINE_TYPE(QtiRadioExt, qti_radio_ext, G_TYPE_OBJECT) + +#define THIS_TYPE qti_radio_ext_get_type() +#define THIS(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, THIS_TYPE, QtiRadioExt) +#define PARENT_CLASS qti_radio_ext_parent_class +#define KEY(serial) GUINT_TO_POINTER(serial) + +typedef struct qti_radio_ext_request QtiRadioExtRequest; + +typedef void (*QtiRadioExtArgWriteFunc)( + GBinderWriter* args, + va_list va); + +typedef void (*QtiRadioExtRequestHandlerFunc)( + QtiRadioExtRequest* req, + const GBinderReader* args); + +typedef void (*QtiRadioExtResultFunc)( + QtiRadioExt* radio, + int result, + void* user_data); + +struct qti_radio_ext_request { + guint id; /* request id */ + gulong tx; /* binder transaction id */ + QtiRadioExt* radio; + gint32 response_code; + QtiRadioExtRequestHandlerFunc handle_response; + void (*free)(QtiRadioExtRequest* req); + GDestroyNotify destroy; + void* user_data; +}; + +typedef struct qti_radio_ext_result_request { + QtiRadioExtRequest base; + QtiRadioExtResultFunc complete; +} QtiRadioExtResultRequest; + +enum qti_radio_ext_signal { + SIGNAL_IMS_REG_STATUS_CHANGED, + SIGNAL_COUNT +}; + +#define SIGNAL_IMS_REG_STATUS_CHANGED_NAME "qti-radio-ext-ims-reg-status-changed" + +static guint qti_radio_ext_signals[SIGNAL_COUNT] = { 0 }; + +static GLogModule qti_radio_ext_binder_log_module = { + .max_level = GLOG_LEVEL_VERBOSE, + .level = GLOG_LEVEL_VERBOSE, + .flags = GLOG_FLAG_HIDE_NAME +}; + +static GLogModule qti_radio_ext_binder_dump_module = { + .parent = &qti_radio_ext_binder_log_module, + .max_level = GLOG_LEVEL_VERBOSE, + .level = GLOG_LEVEL_INHERIT, + .flags = GLOG_FLAG_HIDE_NAME +}; + +static +void +qti_radio_ext_log_notify( + struct ofono_debug_desc* desc) +{ + qti_radio_ext_binder_log_module.level = (desc->flags & + OFONO_DEBUG_FLAG_PRINT) ? GLOG_LEVEL_VERBOSE : GLOG_LEVEL_INHERIT; +} + +static +void +qti_radio_ext_dump_notify( + struct ofono_debug_desc* desc) +{ + qti_radio_ext_binder_dump_module.level = (desc->flags & + OFONO_DEBUG_FLAG_PRINT) ? GLOG_LEVEL_VERBOSE : GLOG_LEVEL_INHERIT; +} + +static struct ofono_debug_desc logger_trace OFONO_DEBUG_ATTR = { + .name = "qti_binder_trace", + .flags = OFONO_DEBUG_FLAG_DEFAULT | OFONO_DEBUG_FLAG_HIDE_NAME, + .notify = qti_radio_ext_log_notify +}; + +static struct ofono_debug_desc logger_dump OFONO_DEBUG_ATTR = { + .name = "qti_binder_dump", + .flags = OFONO_DEBUG_FLAG_DEFAULT | OFONO_DEBUG_FLAG_HIDE_NAME, + .notify = qti_radio_ext_dump_notify +}; + +static +guint +qti_radio_ext_new_req_id() +{ + static guint last_id = 0; + return last_id++; +} + +static +void +qti_radio_ext_log_req( + QtiRadioExt* self, + guint32 code, + guint32 serial) +{ + static const GLogModule* log = &qti_radio_ext_binder_log_module; + const int level = GLOG_LEVEL_VERBOSE; + const char* name; + + if (!gutil_log_enabled(log, level)) + return; + + name = qti_radio_ext_req_name(code); + + if (serial) { + gutil_log(log, level, "%s< [%08x] %u %s", + self->slot, serial, code, name ? name : "???"); + } else { + gutil_log(log, level, "%s< %u %s", + self->slot, code, name ? name : "???"); + } +} + +void +qti_radio_ext_log_resp( + QtiRadioExt* self, + guint32 code, + guint32 serial) +{ + static const GLogModule* log = &qti_radio_ext_binder_log_module; + const int level = GLOG_LEVEL_VERBOSE; + const char* name; + + if (!gutil_log_enabled(log, level)) + return; + + name = qti_radio_ext_resp_name(code); + + gutil_log(log, level, "%s> [%08x] %u %s", + self->slot, serial, code, name ? name : "???"); +} + +static +void +qti_radio_ext_log_ind( + QtiRadioExt* self, + guint32 code) +{ + static const GLogModule* log = &qti_radio_ext_binder_log_module; + const int level = GLOG_LEVEL_VERBOSE; + const char* name; + + if (!gutil_log_enabled(log, level)) + return; + + name = qti_radio_ext_ind_name(code); + + gutil_log(log, level, "%s > %u %s", self->slot, code, + name ? name : "???"); +} + +static +void +qti_radio_ext_dump_data( + const GBinderReader* reader) +{ + static const GLogModule* log = &qti_radio_ext_binder_dump_module; + const int level = GLOG_LEVEL_VERBOSE; + gsize size; + const guint8* data; + + if (!gutil_log_enabled(log, level)) + return; + + data = gbinder_reader_get_data(reader, &size); + gutil_log_dump(log, level, " ", data, size); +} + +static +void +qti_radio_ext_dump_request( + GBinderLocalRequest* args) +{ + static const GLogModule* log = &qti_radio_ext_binder_dump_module; + const int level = GLOG_LEVEL_VERBOSE; + GBinderWriter writer; + const guint8* data; + gsize size; + + if (!gutil_log_enabled(log, level)) + return; + + /* 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(log, level, " ", data, size); +} + +static +const QtiRadioRegInfo* +qti_radio_ext_read_ims_reg_status_info( + QtiRadioExt* self, + GBinderReader* reader) +{ + const QtiRadioRegInfo* info; + + info = gbinder_reader_read_hidl_struct(reader, QtiRadioRegInfo); + if (info) { + const char *uri = info->uri.data.str ? info->uri.data.str : ""; + const char *error_msg = info->error_message.data.str ? info->error_message.data.str : ""; + + DBG("%s: QtiRadioRegInfo state:%d radiotech:%d" + " error_code:%d\n" + " uri:%s error_msg:%s", + self->slot, + info->state, + info->radio_tech, + info->error_code, + uri, error_msg); + + return info; + } else { + DBG("%s: failed to parse QtiRadioRegInfo", self->slot); + return NULL; + } +} + +static +void +qti_radio_ext_handle_ims_reg_status_report( + QtiRadioExt* self, + const GBinderReader* args) +{ + GBinderReader reader; + + gbinder_reader_copy(&reader, args); + const QtiRadioRegInfo* info = + qti_radio_ext_read_ims_reg_status_info(self, &reader); + + g_signal_emit(self, qti_radio_ext_signals[SIGNAL_IMS_REG_STATUS_CHANGED], + 0, info->state); +} + + +static +GBinderLocalReply* +qti_radio_ext_indication( + GBinderLocalObject* obj, + GBinderRemoteRequest* req, + guint code, + guint flags, + int* status, + void* user_data) +{ + QtiRadioExt* self = THIS(user_data); + const char* iface = gbinder_remote_request_interface(req); + GBinderReader args; + + gbinder_remote_request_init_reader(req, &args); + qti_radio_ext_log_ind(self, code); + qti_radio_ext_dump_data(&args); + + if (g_str_equal(iface, QTI_RADIO_INDICATION_1_0)) { + switch(code) { + case QTI_RADIO_IND_REG_STATE_INDICATION: + qti_radio_ext_handle_ims_reg_status_report(self, &args); + return NULL; + } + } + + + return NULL; +} + +gulong +qti_radio_ext_add_ims_reg_status_handler( + QtiRadioExt* self, + QtiRadioExtImsRegStatusFunc handler, + void* user_data) +{ + return (G_LIKELY(self) && G_LIKELY(handler)) ? g_signal_connect(self, + SIGNAL_IMS_REG_STATUS_CHANGED_NAME, G_CALLBACK(handler), user_data) : 0; +} + +static +GBinderLocalReply* +qti_radio_ext_response( + GBinderLocalObject* obj, + GBinderRemoteRequest* req, + guint code, + guint flags, + int* status, + void* user_data) +{ + QtiRadioExt* self = THIS(user_data); + const char* iface = gbinder_remote_request_interface(req); + GBinderReader reader; + guint32 serial = 0; + + gbinder_remote_request_init_reader(req, &reader); + + gbinder_reader_read_uint32(&reader, &serial); + qti_radio_ext_log_resp(self, code, serial); + qti_radio_ext_dump_data(&reader); + + if (serial) { + QtiRadioExtRequest* req = g_hash_table_lookup(self->requests, + KEY(serial)); + + if (req && req->response_code == code) { + g_object_ref(self); + if (req->handle_response) { + req->handle_response(req, &reader); + } + g_hash_table_remove(self->requests, KEY(serial)); + g_object_unref(self); + } else { + DBG("Unexpected response %s %u", iface, code); + *status = GBINDER_STATUS_FAILED; + } + } + + return NULL; +} + +static +void +qti_radio_ext_result_response( + QtiRadioExtRequest* req, + const GBinderReader* args) +{ + gint32 result; + GBinderReader reader; + QtiRadioExt* self = req->radio; + QtiRadioExtResultRequest* result_req = G_CAST(req, + QtiRadioExtResultRequest, base); + + gbinder_reader_copy(&reader, args); + if (!gbinder_reader_read_int32(&reader, &result)) { + ofono_warn("Failed to parse response"); + result = -1; + } + if (result_req->complete) { + result_req->complete(self, result, req->user_data); + } +} + +static +void +qti_radio_ext_request_default_free( + QtiRadioExtRequest* req) +{ + if (req->destroy) { + req->destroy(req->user_data); + } + g_free(req); +} + +static +void +qti_radio_ext_request_destroy( + gpointer user_data) +{ + QtiRadioExtRequest* req = user_data; + + gbinder_client_cancel(req->radio->client, req->tx); + req->free(req); +} + +static +gpointer +qti_radio_ext_request_alloc( + QtiRadioExt* self, + gint32 resp, + QtiRadioExtRequestHandlerFunc handler, + GDestroyNotify destroy, + void* user_data, + gsize size) +{ + QtiRadioExtRequest* req = g_malloc0(size); + + req->radio = self; + req->response_code = resp; + req->handle_response = handler; + req->id = qti_radio_ext_new_req_id(self); + req->free = qti_radio_ext_request_default_free; + req->destroy = destroy; + req->user_data = user_data; + g_hash_table_insert(self->requests, KEY(req->id), req); + return req; +} + +static +QtiRadioExtResultRequest* +qti_radio_ext_result_request_new( + QtiRadioExt* self, + gint32 resp, + QtiRadioExtResultFunc complete, + GDestroyNotify destroy, + void* user_data) +{ + QtiRadioExtResultRequest* req = + (QtiRadioExtResultRequest*)qti_radio_ext_request_alloc(self, resp, + qti_radio_ext_result_response, destroy, user_data, + sizeof(QtiRadioExtResultRequest)); + + req->complete = complete; + return req; +} + +static +void +qti_radio_ext_request_sent( + GBinderClient* client, + GBinderRemoteReply* reply, + int status, + void* user_data) +{ + ((QtiRadioExtRequest*)user_data)->tx = 0; +} + +static +gulong +qti_radio_ext_call( + QtiRadioExt* self, + gint32 code, + gint32 serial, + GBinderLocalRequest* req, + GBinderClientReplyFunc reply, + GDestroyNotify destroy, + void* user_data) +{ + qti_radio_ext_log_req(self, code, serial); + qti_radio_ext_dump_request(req); + + return gbinder_client_transact(self->client, code, + GBINDER_TX_FLAG_ONEWAY, req, reply, destroy, user_data); +} + +static +gulong +qti_radio_ext_submit_request( + QtiRadioExtRequest* request, + gint32 code, + gint32 serial, + GBinderLocalRequest* args) +{ + return (request->tx = qti_radio_ext_call(request->radio, + code, serial, args, qti_radio_ext_request_sent, NULL, request)); +} + +static +guint +qti_radio_ext_result_request_submit( + QtiRadioExt* self, + gint32 req_code, + gint32 resp_code, + QtiRadioExtArgWriteFunc write_args, + QtiRadioExtResultFunc complete, + GDestroyNotify destroy, + void* user_data, + ...) +{ + if (G_LIKELY(self)) { + GBinderLocalRequest* args; + GBinderWriter writer; + QtiRadioExtResultRequest* req = + qti_radio_ext_result_request_new(self, resp_code, + complete, destroy, user_data); + const guint req_id = req->base.id; + + args = gbinder_client_new_request2(self->client, req_code); + gbinder_local_request_init_writer(args, &writer); + gbinder_writer_append_int32(&writer, req_id); + if (write_args) { + va_list va; + + va_start(va, user_data); + write_args(&writer, va); + va_end(va); + } + + /* Submit the request */ + qti_radio_ext_submit_request(&req->base, req_code, req_id, args); + gbinder_local_request_unref(args); + if (req->base.tx) { + /* Success */ + return req_id; + } + g_hash_table_remove(self->requests, KEY(req_id)); + } + return 0; +} + +static +QtiRadioExt* +qti_radio_ext_create( + GBinderServiceManager* sm, + GBinderRemoteObject* remote, + const char* slot) +{ + QtiRadioExt* self = g_object_new(THIS_TYPE, NULL); + const gint code = QTI_RADIO_REQ_SET_CALLBACK; + GBinderLocalRequest* req; + GBinderWriter writer; + int status; + + self->slot = g_strdup(slot); + self->client = gbinder_client_new(remote, QTI_RADIO_1_0); + self->response = gbinder_servicemanager_new_local_object(sm, + QTI_RADIO_RESPONSE_1_0, qti_radio_ext_response, self); + self->indication = gbinder_servicemanager_new_local_object(sm, + QTI_RADIO_INDICATION_1_0, qti_radio_ext_indication, self); + + req = gbinder_client_new_request2(self->client, code); + gbinder_local_request_init_writer(req, &writer); + gbinder_writer_append_local_object(&writer, self->response); + gbinder_writer_append_local_object(&writer, self->indication); + + qti_radio_ext_log_req(self, code, 0 /*serial*/); + qti_radio_ext_dump_request(req); + gbinder_remote_reply_unref(gbinder_client_transact_sync_reply(self->client, + code, req, &status)); + + DBG("setResponseFunctions status %d", status); + gbinder_local_request_unref(req); + return self; +} + +/*==========================================================================* + * API + *==========================================================================*/ + + +QtiRadioExt* +qti_radio_ext_new( + const char* dev, + const char* slot) +{ + QtiRadioExt* self = NULL; + + GBinderServiceManager* sm = gbinder_servicemanager_new(dev); + if (sm) { + char* fqname = g_strconcat(QTI_RADIO_1_0, "/", slot, NULL); + GBinderRemoteObject* obj = /* autoreleased */ + gbinder_servicemanager_get_service_sync(sm, fqname, NULL); + + if (obj) { + DBG("Connected to %s", fqname); + self = qti_radio_ext_create(sm, obj, slot); + } else { + DBG("Failed to get %s", fqname); + } + + g_free(fqname); + gbinder_servicemanager_unref(sm); + } + + return self; +} + +QtiRadioExt* +qti_radio_ext_ref( + QtiRadioExt* self) +{ + if (G_LIKELY(self)) { + g_object_ref(self); + } + return self; +} + +void +qti_radio_ext_unref( + QtiRadioExt* self) +{ + if (G_LIKELY(self)) { + g_object_unref(self); + } +} + +static +void +qti_radio_ext_set_reg_state_args( + GBinderWriter* args, + va_list va) +{ + gbinder_writer_append_int32(args, va_arg(va, gint32)); +} + +// BINDER_EXT_IMS_REGISTRATION to QTI_RADIO_REG_STATE +static +QTI_RADIO_REG_STATE +qti_radio_ext_reg_state( + BINDER_EXT_IMS_REGISTRATION state) +{ + switch (state) { + case BINDER_EXT_IMS_REGISTRATION_ON: + return QTI_RADIO_REG_STATE_REGISTERED; + case BINDER_EXT_IMS_REGISTRATION_OFF: + return QTI_RADIO_REG_STATE_NOT_REGISTERED; + default: + return QTI_RADIO_REG_STATE_INVALID; + } +} + + +guint +qti_radio_ext_set_reg_state( + QtiRadioExt* self, + BINDER_EXT_IMS_REGISTRATION state, + QtiRadioExtResultFunc complete, + GDestroyNotify destroy, + void* user_data) +{ + QTI_RADIO_REG_STATE reg_state = qti_radio_ext_reg_state(state); + + DBG("Setting registration state %d", reg_state); + + return qti_radio_ext_result_request_submit(self, + QTI_RADIO_REQ_REQ_REG_CHANGE, + QTI_RADIO_RESP_REQ_REG_CHANGE, + qti_radio_ext_set_reg_state_args, + complete, destroy, user_data, + reg_state); +} + +/*==========================================================================* + * Internals + *==========================================================================*/ + +static +void +qti_radio_ext_finalize( + GObject* object) +{ + QtiRadioExt* self = THIS(object); + + g_free(self->slot); + G_OBJECT_CLASS(PARENT_CLASS)->finalize(object); +} + +static +void +qti_radio_ext_init( + QtiRadioExt* self) +{ + self->pool = gutil_idle_pool_new(); + self->requests = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, + qti_radio_ext_request_destroy); +} + +static +void +qti_radio_ext_class_init( + QtiRadioExtClass* klass) +{ + G_OBJECT_CLASS(klass)->finalize = qti_radio_ext_finalize; + qti_radio_ext_signals[SIGNAL_IMS_REG_STATUS_CHANGED] = + g_signal_new(SIGNAL_IMS_REG_STATUS_CHANGED_NAME, G_OBJECT_CLASS_TYPE(klass), + G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, G_TYPE_NONE, + 1, G_TYPE_UINT); +} + +/* + * Local Variables: + * mode: C + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/src/qti_radio_ext.h b/src/qti_radio_ext.h new file mode 100644 index 0000000..8335f8d --- /dev/null +++ b/src/qti_radio_ext.h @@ -0,0 +1,73 @@ +/* + * oFono - Open Source Telephony - binder based adaptation QTI plugin + * + * Copyright (C) 2024 TheKit + * + * 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 QTI_RADIO_EXT_H +#define QTI_RADIO_EXT_H + +#include +#include + +#include "qti_radio_ext_types.h" + + +typedef struct qti_radio_ext QtiRadioExt; + +typedef void (*QtiRadioExtResultFunc)( + QtiRadioExt* radio, + int result, + void* user_data); + +typedef void (*QtiRadioExtImsRegStatusFunc)( + QtiRadioExt* radio, + guint32 status, + void* user_data); + +QtiRadioExt* +qti_radio_ext_new( + const char* dev, + const char* slot); + +QtiRadioExt* +qti_radio_ext_ref( + QtiRadioExt* self); + +void +qti_radio_ext_unref( + QtiRadioExt* self); + +guint +qti_radio_ext_set_reg_state( + QtiRadioExt* self, + BINDER_EXT_IMS_REGISTRATION state, + QtiRadioExtResultFunc complete, + GDestroyNotify destroy, + void* user_data); + +gulong +qti_radio_ext_add_ims_reg_status_handler( + QtiRadioExt* self, + QtiRadioExtImsRegStatusFunc handler, + void* user_data); + + +#endif /* QTI_RADIO_EXT_H */ + +/* + * Local Variables: + * mode: C + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/src/qti_radio_ext_types.h b/src/qti_radio_ext_types.h new file mode 100644 index 0000000..6bdccb6 --- /dev/null +++ b/src/qti_radio_ext_types.h @@ -0,0 +1,200 @@ +/* + * oFono - Open Source Telephony - binder based adaptation QTI plugin + * + * Copyright (C) 2024 TheKit + * + * 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 QTI_RADIO_EXT_TYPES_H +#define QTI_RADIO_EXT_TYPES_H + +#define QTI_RADIO_IFACE "IImsRadio" +#define QTI_RADIO_RESPONSE_IFACE "IImsRadioResponse" +#define QTI_RADIO_INDICATION_IFACE "IImsRadioIndication" +#define QTI_RADIO_IFACE_PREFIX "vendor.qti.hardware.radio.ims@" + +#define QTI_RADIO_IFACE_1_0(x) QTI_RADIO_IFACE_PREFIX "1.0::" x +#define QTI_RADIO_IFACE_1_1(x) QTI_RADIO_IFACE_PREFIX "1.1::" x +#define QTI_RADIO_IFACE_1_2(x) QTI_RADIO_IFACE_PREFIX "1.2::" x + +#define QTI_RADIO_1_0 QTI_RADIO_IFACE_1_0(QTI_RADIO_IFACE) +#define QTI_RADIO_1_1 QTI_RADIO_IFACE_1_1(QTI_RADIO_IFACE) +#define QTI_RADIO_1_2 QTI_RADIO_IFACE_1_2(QTI_RADIO_IFACE) + +#define QTI_RADIO_RESPONSE_1_0 QTI_RADIO_IFACE_1_0(QTI_RADIO_RESPONSE_IFACE) +#define QTI_RADIO_RESPONSE_1_1 QTI_RADIO_IFACE_1_1(QTI_RADIO_RESPONSE_IFACE) +#define QTI_RADIO_RESPONSE_1_2 QTI_RADIO_IFACE_1_2(QTI_RADIO_RESPONSE_IFACE) + +#define QTI_RADIO_INDICATION_1_0 QTI_RADIO_IFACE_1_0(QTI_RADIO_INDICATION_IFACE) +#define QTI_RADIO_INDICATION_1_1 QTI_RADIO_IFACE_1_1(QTI_RADIO_INDICATION_IFACE) +#define QTI_RADIO_INDICATION_1_2 QTI_RADIO_IFACE_1_2(QTI_RADIO_INDICATION_IFACE) + +/* +enum RegState : int32_t { + REGISTERED, + NOT_REGISTERED, + REGISTERING, + INVALID, +}; +*/ + +typedef enum qti_radio_reg_state { + QTI_RADIO_REG_STATE_REGISTERED = 0, + QTI_RADIO_REG_STATE_NOT_REGISTERED = 1, + QTI_RADIO_REG_STATE_REGISTERING = 2, + QTI_RADIO_REG_STATE_INVALID = 3, +} QTI_RADIO_REG_STATE; + +/* +enum StatusType : int32_t { + STATUS_DISABLED, + STATUS_PARTIALLY_ENABLED, + STATUS_ENABLED, + STATUS_NOT_SUPPORTED, + STATUS_INVALID, +}; +*/ + +typedef enum qti_radio_status { + QTI_RADIO_STATUS_DISABLED = 0, + QTI_RADIO_STATUS_PARTIALLY_ENABLED = 1, + QTI_RADIO_STATUS_ENABLED = 2, + QTI_RADIO_STATUS_NOT_SUPPORTED = 3, + QTI_RADIO_STATUS_INVALID = 4, +} QTI_RADIO_STATUS; + +/* +enum ServiceClassStatus : int32_t { + DISABLED, + ENABLED, + INVALID, +}; +*/ + +typedef enum qti_radio_service_status { + QTI_RADIO_SERVICE_STATUS_DISABLED = 0, + QTI_RADIO_SERVICE_STATUS_ENABLED = 1, + QTI_RADIO_SERVICE_STATUS_INVALID = 2, +} QTI_RADIO_SERVICE_STATUS; + +/* + +struct RegistrationInfo { + RegState state; + uint32_t errorCode; + string errorMessage; + RadioTechType radioTech; + string pAssociatedUris; +}; +*/ + +typedef struct qti_radio_reg_info { + QTI_RADIO_REG_STATE state RADIO_ALIGNED(4); + guint32 error_code RADIO_ALIGNED(4); + GBinderHidlString error_message RADIO_ALIGNED(8); + guint32 radio_tech RADIO_ALIGNED(4); + GBinderHidlString uri RADIO_ALIGNED(8); +} QtiRadioRegInfo; + +/* c(req, resp, callName, CALL_NAME) */ +#define QTI_RADIO_EXT_IMS_CALL_1_0(c) \ + c(2, 1, dail, DAIL) \ + c(4, 11, getImsRegistrationState, GET_IMS_REG_STATE) \ + c(7, 4, requestRegistrationChange, REQ_REG_CHANGE) \ + c(31, 28, setSuppServiceNotification, SET_SUPP_SVC_NOTIFICATION) + +#define QTI_RADIO_EXT_IMS_CALL_1_1(c) \ + c(41, 3, hangup_1_1, HANGUP_1_1) + +#define QTI_RADIO_EXT_IMS_CALL_1_2(c) \ + c(42, 3, hangup_1_2, HANGUP_1_2) \ + c(43, 37, sendImsSms, SEND_IMS_SMS) \ + c(1, 1, acknowledgeSms, ACK_SMS) + +typedef enum qti_radio_req { + /* vendor.mediatek.hardware.qtiradioex@1.0::IqtiRadioExt */ + QTI_RADIO_REQ_SET_CALLBACK = 1, /* setCallback */ +#define QTI_RADIO_REQ_(req,resp,Name,NAME) QTI_RADIO_REQ_##NAME = req, + QTI_RADIO_EXT_IMS_CALL_1_0(QTI_RADIO_REQ_) +#undef QTI_RADIO_REQ_ +} QTI_RADIO_REQ; + +typedef enum ims_radio_resp { + /* vendor.mediatek.hardware.qtiradioex@3.0::IImsRadioResponse */ +#define QTI_RADIO_RESP_(req,resp,Name,NAME) QTI_RADIO_RESP_##NAME = resp, + QTI_RADIO_EXT_IMS_CALL_1_0(QTI_RADIO_RESP_) +#undef QTI_RADIO_RESP_ +} IMS_RADIO_RESP; + +/* e(code, name, NAME) */ +#define QTI_RADIO_IND_1_0(e) \ + e(1, onCallStateChanged, CALL_STATE_INDICATION) \ + e(2, onRing, RING_INDICATION) \ + e(3, onRingbackTone, RINGBACK_TONE_INDICATION) \ + e(4, onRegistrationChanged, REG_STATE_INDICATION) \ + e(5, onHandover, HANDOVER_INDICATION) \ + e(6, onServiceStatusChanged, SVC_STATUS_INDICATION) + +typedef enum ims_radio_ind { + /* vendor.mediatek.hardware.qtiradioex@3.0::IImsRadioIndication */ +#define QTI_RADIO_IND_(code, name, NAME) QTI_RADIO_IND_##NAME = code, + QTI_RADIO_IND_1_0(QTI_RADIO_IND_) +#undef QTI_RADIO_IND_ +} IMS_RADIO_IND; + +static const char* +qti_radio_ext_req_name( + guint32 req) +{ + switch (req) { +#define QTI_RADIO_REQ_(req, resp, name, NAME) \ + case QTI_RADIO_REQ_##NAME: return #name; + QTI_RADIO_EXT_IMS_CALL_1_0(QTI_RADIO_REQ_) +#undef QTI_RADIO_REQ_ + } + return NULL; +} + +static const char* +qti_radio_ext_resp_name( + guint32 resp) +{ + switch (resp) { +#define QTI_RADIO_RESP_(req, resp, name, NAME) \ + case QTI_RADIO_RESP_##NAME: return #name; + QTI_RADIO_EXT_IMS_CALL_1_0(QTI_RADIO_RESP_) +#undef QTI_RADIO_RESP_ + } + return NULL; +} + +static const char* +qti_radio_ext_ind_name( + guint32 ind) +{ + switch (ind) { +#define QTI_RADIO_IND_(code, name, NAME) \ + case QTI_RADIO_IND_##NAME: return #name; + QTI_RADIO_IND_1_0(QTI_RADIO_IND_) +#undef QTI_RADIO_IND_ + } + return NULL; +} + +#endif /* QTI_RADIO_EXT_TYPES_H */ + +/* + * Local Variables: + * mode: C + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/src/qti_slot.c b/src/qti_slot.c index 6b7a6cf..dcc1976 100644 --- a/src/qti_slot.c +++ b/src/qti_slot.c @@ -37,6 +37,7 @@ #include "qti_slot.h" #include "qti_ims.h" +#include "qti_radio_ext.h" #include @@ -46,6 +47,7 @@ typedef BinderExtSlotClass QtiSlotClass; typedef struct qti_slot { BinderExtSlot parent; BinderExtIms* ims; + QtiRadioExt* radio_ext; } QtiSlot; GType qti_slot_get_type() G_GNUC_INTERNAL; @@ -105,8 +107,13 @@ qti_slot_new( { QtiSlot* self = g_object_new(THIS_TYPE, NULL); BinderExtSlot* slot = &self->parent; + char* radio_slot = g_strdup_printf("imsradio%d", radio->slot_index); + + self->radio_ext = qti_radio_ext_new(radio->dev, radio_slot); + if (self->radio_ext) { + self->ims = qti_ims_new(radio_slot, self->radio_ext); + } - self->ims = qti_ims_new(radio->slot); return slot; }