Files
2026-08-24 10:03:24 +08:00

343 lines
9.9 KiB
C++

/*
* Copyright (C) 2014-present Amlogic, Inc. All rights reserved.
*
* All information contained herein is Amlogic confidential.
*
* This software is provided to you pursuant to Software License Agreement
* (SLA) with Amlogic Inc ("Amlogic"). This software may be used
* only in accordance with the terms of this agreement.
*
* Redistribution and use in source and binary forms, with or without
* modification is strictly prohibited without prior written permission from
* Amlogic.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <future>
#include <mutex>
#include <poll.h>
#include <thread>
#include <vector>
#include <systemd/sd-bus.h>
#include "dbus_defines.h"
#define LOG_MODULE_TAG "PQ"
#define LOG_CLASS_TAG "PqService"
#include "CPQLog.h"
#include "PqClient.h"
static sd_bus *gs_signal_bus = nullptr;
bool pqclient_preset_signal_bus(sd_bus *bus) {
if (nullptr == bus) {
LOGE("invalid parameter\n");
return false;
}
if (nullptr != gs_signal_bus) {
LOGE("signal bus already set\n");
return false;
}
gs_signal_bus = bus;
return true;
}
static int gs_method_call_pipe[2];
static std::vector<PqClient::PqClientIObserver *> mPqClientObserver;
static int SendEvent(CPqClientCb &cb_data) {
for (const auto &o : mPqClientObserver) {
if (o != nullptr) {
o->GetPqCbData(cb_data);
LOGD("send event for %p count PqClientObserver!\n", o);
}
}
return 0;
}
static int signalCallback(sd_bus_message *msg, void *userdata,
sd_bus_error *ret_error) {
const char *memb = sd_bus_message_get_member(msg);
if (strcmp(memb, PQ_SERVICE_SIGNAL_HDR_TYPE) == 0) {
PqClientCb::HdrTypeCb cb;
int r = sd_bus_message_read(msg, "i", &cb.mHdrType);
if (r < 0)
return r;
SendEvent(cb);
} else if (strcmp(memb, PQ_SERVICE_SIGNAL_ALLM_GAME_MODE) == 0) {
PqClientCb::AllmGameModeCb cb;
int r = sd_bus_message_read(msg, "i", &cb.mAllmGameMode);
if (r < 0)
return r;
SendEvent(cb);
} else if (strcmp(memb, PQ_SERVICE_SIGNAL_FILM_MAKER_MODE) == 0) {
PqClientCb::FilmMakerModeCb cb;
int r = sd_bus_message_read(msg, "i", &cb.mFilmMakerMode);
if (r < 0)
return r;
SendEvent(cb);
} else if (strcmp(memb, PQ_SERVICE_SIGNAL_REFRESH_RATE) == 0) {
PqClientCb::RefreshRateCb cb;
int r = sd_bus_message_read(msg, "i", &cb.mRefreshRate);
if (r < 0)
return r;
SendEvent(cb);
} else if (strcmp(memb, PQ_SERVICE_SIGNAL_PICTURE_MODE) == 0) {
PqClientCb::PqModeChangedCb cb;
int r = sd_bus_message_read(msg, "iiii", &cb.pq_mode_info.pq_mode, &cb.pq_mode_info.hdr_type,
&cb.pq_mode_info.input_src, &cb.pq_mode_info.low_latency);
if (r < 0)
return r;
SendEvent(cb);
} else if (strcmp(memb, PQ_SERVICE_SIGNAL_PICTURE_MODE_MANUAL) == 0) {
PqClientCb::PictureModeCb cb;
int r = sd_bus_message_read(msg, "i", &cb.mPictureMode);
if (r < 0)
return r;
SendEvent(cb);
} else {
LOGE("invalid signal received!\n");
}
return 0;
}
static void bus_cleanup(sd_bus *bus, sd_event *event) {
if (event) {
sd_event_exit(event, 0);
sd_event_unref(event);
event = nullptr;
}
if (bus) {
sd_bus_unref(bus);
bus = nullptr;
}
}
using DeferCallbackHandler = std::function<void(sd_bus *, sd_event *)>;
struct defer_callback_data {
sd_bus *bus;
sd_event *event;
const DeferCallbackHandler *handler;
defer_callback_data(sd_bus *b, sd_event *e, const DeferCallbackHandler *h)
: bus(b), event(e), handler(h) {}
};
static int defer_callback(sd_event_source *, void *userdata) {
auto data = static_cast<defer_callback_data *>(userdata);
(*data->handler)(data->bus, data->event);
delete data;
return 0;
}
static void init_run_bus(DeferCallbackHandler callback) {
std::thread t([cb = std::move(callback)]() {
sd_bus *bus = nullptr;
sd_event *event = nullptr;
int r = sd_bus_default_system(&bus);
if (r < 0) {
LOGE("Failed to open system bus: %s\n", strerror(-r));
return;
}
r = sd_event_default(&event);
if (r < 0) {
LOGE("Failed to create event: %s\n", strerror(-r));
sd_bus_unref(bus);
return;
}
r = sd_bus_attach_event(bus, event, 0);
if (r < 0) {
LOGE("Failed to attach event: %s\n", strerror(-r));
sd_event_unref(event);
sd_bus_unref(bus);
return;
}
if (cb) {
// Add a defer event
r = sd_event_add_defer(event, nullptr, defer_callback,
new defer_callback_data(bus, event, &cb));
if (r < 0) {
LOGE("Failed to add defer event: %s\n", strerror(-r));
sd_event_unref(event);
sd_bus_unref(bus);
return;
}
}
sd_event_loop(event);
bus_cleanup(bus, event);
});
t.detach();
}
using onCallPrepareHandler = std::function<bool(sd_bus_message *)>;
using onCallDoneHandler = std::function<int(sd_bus_message *)>;
struct MethodCallRequest {
const char *method;
onCallPrepareHandler *callPrepare;
onCallDoneHandler *onCallDone;
MethodCallRequest()
: method(nullptr), callPrepare(nullptr), onCallDone(nullptr) {}
MethodCallRequest(const char *m, onCallPrepareHandler *cp,
onCallDoneHandler *ocd)
: method(m), callPrepare(cp), onCallDone(ocd) {}
};
static int on_call_done(sd_bus_message *m, void *userdata,
sd_bus_error *ret_error) {
auto handler = static_cast<onCallDoneHandler *>(userdata);
const sd_bus_error *err = sd_bus_message_get_error(m);
if (err) {
LOGE("Failed to call: %s, %s(%s)\n", strerror(-sd_bus_error_get_errno(err)),
err->name, err->message);
return (*handler)(nullptr);
}
return (*handler)(m);
}
static int method_call_handler(sd_event_source *, int fd, uint32_t /*revents*/,
void *userdata) {
sd_bus *bus = (sd_bus *)userdata;
MethodCallRequest req;
int len = read(fd, &req, sizeof(req));
if (len != sizeof(req)) {
LOGE("%d read, %d bytes expected\n", len, sizeof(req));
} else {
if (req.method == nullptr)
return 0;
sd_bus_message *msg = nullptr;
if (sd_bus_message_new_method_call(
bus, &msg, PQ_SERVICE_DBUS_SERVICE, PQ_SERVICE_DBUS_OBJECT,
PQ_SERVICE_DBUS_INTERFACE, req.method) < 0)
return 0;
if (req.callPrepare && !(*req.callPrepare)(msg)) {
sd_bus_message_unref(msg);
msg = nullptr;
return 0;
}
int r;
if ((r = sd_bus_call_async(bus, nullptr, msg, on_call_done, req.onCallDone,
0)) < 0) {
LOGE("Failed to async call: %s (%s)\n", req.method, strerror(-r));
(*req.onCallDone)(nullptr);
}
if (msg) {
sd_bus_message_unref(msg);
msg = nullptr;
}
}
return 0;
}
static sd_bus_message *
call_method(const char *member, std::function<bool(sd_bus_message *)> packer) {
std::promise<sd_bus_message *> b;
std::future<sd_bus_message *> f = b.get_future();
onCallPrepareHandler prepare(
[=](sd_bus_message *msg) -> bool { return packer(msg); });
onCallDoneHandler done([&b](sd_bus_message *m) -> int {
sd_bus_message_ref(m);
b.set_value(m);
return 0;
});
MethodCallRequest req(member, packer ? &prepare : nullptr, &done);
if (write(gs_method_call_pipe[1], &req, sizeof(req)) != sizeof(req)) {
LOGE("Failed to write to pipe: %s\n", strerror(errno));
return nullptr;
}
sd_bus_message *msg = f.get();
return msg;
}
#include "dbus_client_api_impl.cpp"
PqClient::PqClient() {
init_pq_logging();
if (gs_signal_bus) {
int r = sd_bus_match_signal(
gs_signal_bus, nullptr, PQ_SERVICE_DBUS_SERVICE, PQ_SERVICE_DBUS_OBJECT,
PQ_SERVICE_DBUS_INTERFACE, nullptr, signalCallback, nullptr);
if (r < 0) {
LOGE("Failed to match signal: %s\n", strerror(-r));
}
} else {
init_run_bus([](sd_bus *bus, sd_event *) {
int r = sd_bus_match_signal(
bus, nullptr, PQ_SERVICE_DBUS_SERVICE, PQ_SERVICE_DBUS_OBJECT,
PQ_SERVICE_DBUS_INTERFACE, nullptr, signalCallback, nullptr);
if (r < 0) {
LOGE("Failed to match signal: %s\n", strerror(-r));
}
});
}
std::promise<bool> b;
std::future<bool> f = b.get_future();
init_run_bus([&](sd_bus *bus, sd_event *event) {
if (pipe(gs_method_call_pipe) < 0) {
LOGE("pipe() failed: %s\n", strerror(errno));
return;
}
sd_event_add_io(event, nullptr, gs_method_call_pipe[0], POLLIN,
method_call_handler, bus);
try {
b.set_value(true);
} catch (const std::future_error &e) {
LOGE("%s\n", e.what());
}
});
f.get();
}
PqClient::~PqClient() {
LOGD("%s: PqClient has been destructed.\n", __FUNCTION__);
}
int PqClient::RegisterObserverToPqClient(PqClientIObserver *observer) {
if (observer != nullptr) {
mPqClientObserver.emplace_back(observer);
LOGD("%s: mPqClientObserver[%p] has been register.\n", __FUNCTION__,
observer);
} else {
LOGD("%s: observer is NULL.\n", __FUNCTION__);
}
return 0;
}
PqClient *PqClient::GetInstance() {
static std::shared_ptr<PqClient> inst_;
if (inst_.get() == nullptr) {
inst_ = std::shared_ptr<PqClient>(new PqClient(),
[](PqClient *ptr) { delete ptr; });
}
return inst_.get();
}