2 This file is part of PulseAudio.
4 Copyright 2021 Jaechul Lee <jcsing.lee@samsung.com>
6 PulseAudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published
8 by the Free Software Foundation; either version 2.1 of the License,
9 or (at your option) any later version.
11 PulseAudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with PulseAudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
26 #include <pulsecore/sink.h>
27 #include <pulsecore/source.h>
28 #include <pulsecore/core-util.h>
29 #include <pulsecore/core-error.h>
30 #include <pulsecore/macro.h>
32 #include <pulsecore/dbus-shared.h>
33 #include <pulsecore/protocol-dbus.h>
34 #include <pulsecore/dbus-util.h>
36 #include "aec-manager.h"
38 #define AEC_MANAGER_OBJECT_PATH "/org/pulseaudio/AecManager"
39 #define AEC_MANAGER_INTERFACE "org.pulseaudio.AecManager"
41 #define AEC_MANAGER_METHOD_NAME_ON "On"
42 #define AEC_MANAGER_METHOD_NAME_OFF "Off"
43 #define AEC_MANAGER_METHOD_NAME_GET_SINK_PARAMS "GetSinkParam"
44 #define AEC_MANAGER_METHOD_NAME_GET_SOURCE_PARAMS "GetSourceParam"
46 #define AEC_UNIX_SOCKET_PATH "/tmp/.aec.socket"
48 static struct aec_manager {
49 pa_dbus_connection *dbus_conn;
50 pa_source *source; /* builtin-mic */
51 pa_sink *sink; /* builtin-spk */
55 SINK_MESSAGE_SET_AEC_STATE = PA_SINK_MESSAGE_MAX,
56 SINK_MESSAGE_GET_AEC_PARAMS,
60 SOURCE_MESSAGE_SET_AEC_STATE = PA_SOURCE_MESSAGE_MAX,
61 SOURCE_MESSAGE_GET_AEC_PARAMS,
64 #define AEC_MANAGER_INTROSPECT_XML \
65 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
67 " <interface name=\"AEC_MANAGER_INTERFACE\">" \
68 " <method name=\"AEC_MANAGER_METHOD_NAME_ON\">" \
70 " <method name=\"AEC_MANAGER_METHOD_NAME_OFF\">" \
72 " <method name=\"AEC_MANAGER_METHOD_NAME_GET_SINK_PARAMS\">" \
73 " <arg name=\"rate\" direction=\"out\" type=\"i\"/>" \
74 " <arg name=\"channels\" direction=\"out\" type=\"i\"/>" \
75 " <arg name=\"format\" direction=\"out\" type=\"i\"/>" \
76 " <arg name=\"frag_size\" direction=\"out\" type=\"i\"/>" \
77 " <arg name=\"nfrags\" direction=\"out\" type=\"i\"/>" \
78 " <arg name=\"card\" direction=\"out\" type=\"s\"/>" \
79 " <arg name=\"device\" direction=\"out\" type=\"s\"/>" \
81 " <method name=\"AEC_MANAGER_METHOD_NAME_GET_SOURCE_PARAMS\">" \
82 " <arg name=\"rate\" direction=\"out\" type=\"i\"/>" \
83 " <arg name=\"channels\" direction=\"out\" type=\"i\"/>" \
84 " <arg name=\"format\" direction=\"out\" type=\"i\"/>" \
85 " <arg name=\"frag_size\" direction=\"out\" type=\"i\"/>" \
86 " <arg name=\"nfrags\" direction=\"out\" type=\"i\"/>" \
87 " <arg name=\"card\" direction=\"out\" type=\"s\"/>" \
88 " <arg name=\"device\" direction=\"out\" type=\"s\"/>" \
91 " <interface name=\"org.freedesktop.DBus.Introspectable\">" \
92 " <method name=\"Introspect\">" \
93 " <arg name=\"data\" type=\"s\" direction=\"out\"/>" \
98 enum method_handler_index {
101 METHOD_HANDLER_GET_SINK_PARAMS,
102 METHOD_HANDLER_GET_SOURCE_PARAMS,
106 static void handle_method_on(DBusConnection *conn, DBusMessage *msg, void *userdata);
107 static void handle_method_off(DBusConnection *conn, DBusMessage *msg, void *userdata);
108 static void handle_method_get_sink_params(DBusConnection *conn, DBusMessage *msg, void *userdata);
109 static void handle_method_get_source_params(DBusConnection *conn, DBusMessage *msg, void *userdata);
111 static pa_dbus_method_handler method_handlers[METHOD_HANDLER_MAX] = {
112 [METHOD_HANDLER_ON] = {
113 .method_name = AEC_MANAGER_METHOD_NAME_ON,
114 .receive_cb = handle_method_on },
115 [METHOD_HANDLER_OFF] = {
116 .method_name = AEC_MANAGER_METHOD_NAME_OFF,
117 .receive_cb = handle_method_off },
118 [METHOD_HANDLER_GET_SINK_PARAMS] = {
119 .method_name = AEC_MANAGER_METHOD_NAME_GET_SINK_PARAMS,
120 .receive_cb = handle_method_get_sink_params },
121 [METHOD_HANDLER_GET_SOURCE_PARAMS] = {
122 .method_name = AEC_MANAGER_METHOD_NAME_GET_SOURCE_PARAMS,
123 .receive_cb = handle_method_get_source_params },
126 static void send_get_param_reply(DBusConnection *conn, DBusMessage *msg, aec_params_t* param) {
127 DBusMessage *reply = NULL;
128 DBusMessageIter msg_iter;
130 const char *ptr1 = param->card;
131 const char *ptr2 = param->device;
133 reply = dbus_message_new_method_return(msg);
135 pa_log_error("Failed to alloc reply");
139 dbus_message_iter_init_append(reply, &msg_iter);
140 dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_INT32, ¶m->rate);
141 dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_INT32, ¶m->channels);
142 dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_INT32, ¶m->format);
143 dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_INT32, ¶m->frag_size);
144 dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_INT32, ¶m->nfrags);
145 dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_STRING, &ptr1);
146 dbus_message_iter_append_basic(&msg_iter, DBUS_TYPE_STRING, &ptr2);
147 dbus_connection_send(conn, reply, NULL);
149 dbus_message_unref(reply);
152 static void handle_method_on(DBusConnection *conn, DBusMessage *msg, void *userdata) {
153 pa_asyncmsgq_post(am.sink->asyncmsgq, PA_MSGOBJECT(am.sink),
154 SINK_MESSAGE_SET_AEC_STATE, (void *)TRUE, 0, NULL, NULL);
155 pa_asyncmsgq_post(am.source->asyncmsgq, PA_MSGOBJECT(am.source),
156 SOURCE_MESSAGE_SET_AEC_STATE, (void *)TRUE, 0, NULL, NULL);
158 pa_dbus_send_empty_reply(conn, msg);
161 static void handle_method_off(DBusConnection *conn, DBusMessage *msg, void *userdata) {
162 pa_asyncmsgq_post(am.sink->asyncmsgq, PA_MSGOBJECT(am.sink),
163 SINK_MESSAGE_SET_AEC_STATE, (void *)FALSE, 0, NULL, NULL);
164 pa_asyncmsgq_post(am.source->asyncmsgq, PA_MSGOBJECT(am.source),
165 SOURCE_MESSAGE_SET_AEC_STATE, (void *)FALSE, 0, NULL, NULL);
167 pa_dbus_send_empty_reply(conn, msg);
170 static void handle_method_get_sink_params(DBusConnection *conn, DBusMessage *msg, void *userdata) {
173 pa_asyncmsgq_send(am.sink->asyncmsgq, PA_MSGOBJECT(am.sink),
174 SINK_MESSAGE_GET_AEC_PARAMS, ¶m, 0, NULL);
176 send_get_param_reply(conn, msg, ¶m);
179 static void handle_method_get_source_params(DBusConnection *conn, DBusMessage *msg, void *userdata) {
182 pa_asyncmsgq_send(am.source->asyncmsgq, PA_MSGOBJECT(am.source),
183 SOURCE_MESSAGE_GET_AEC_PARAMS, ¶m, 0, NULL);
185 send_get_param_reply(conn, msg, ¶m);
188 static DBusHandlerResult method_handler_for_vt(DBusConnection *conn, DBusMessage *m, void *userdata) {
189 const char *path, *interface, *member;
190 DBusMessage *r = NULL;
195 path = dbus_message_get_path(m);
196 interface = dbus_message_get_interface(m);
197 member = dbus_message_get_member(m);
199 pa_log_debug("dbus: path=%s, interface=%s, member=%s", path, interface, member);
201 if (!pa_safe_streq(path, AEC_MANAGER_OBJECT_PATH))
202 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
204 if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
205 r = dbus_message_new_method_return(m);
207 const char *xml = AEC_MANAGER_INTROSPECT_XML;
208 dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID);
209 dbus_connection_send((conn), r, NULL);
210 dbus_message_unref(r);
214 for (i = 0; i < METHOD_HANDLER_MAX; i++) {
215 if (dbus_message_is_method_call(m, AEC_MANAGER_INTERFACE,
216 method_handlers[i].method_name))
217 method_handlers[i].receive_cb(conn, m, NULL);
221 return DBUS_HANDLER_RESULT_HANDLED;
224 int aec_manager_init(pa_core *core, pa_source *source, pa_sink *sink) {
226 pa_dbus_connection *conn = NULL;
227 static const DBusObjectPathVTable vtable = {
228 .message_function = method_handler_for_vt,
231 if (!source || !sink) {
232 pa_log_error("AEC init failed. source(%p)/sink(%p) is null.", source, sink);
236 dbus_error_init(&err);
237 if (!(conn = pa_dbus_bus_get(core, DBUS_BUS_SYSTEM, &err)) || dbus_error_is_set(&err)) {
239 pa_dbus_connection_unref(conn);
241 dbus_error_free(&err);
242 pa_log_error("Unable to contact D-Bus system bus: %s: %s", err.name, err.message);
247 if (!dbus_connection_register_object_path(pa_dbus_connection_get(conn),
248 AEC_MANAGER_OBJECT_PATH, &vtable, NULL)) {
249 pa_dbus_connection_unref(conn);
250 pa_log_error("Failed to register object path");
257 pa_log_info("AEC init success");
262 void aec_manager_deinit(void) {
264 pa_dbus_connection_unref(am.dbus_conn);
266 pa_log_info("AEC deinit success");