1/***
2  This file is part of PulseAudio.
3
4  Copyright 2009 Tanu Kaskinen
5
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.
10
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.
15
16  You should have received a copy of the GNU Lesser General Public License
17  along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
18***/
19
20#ifdef HAVE_CONFIG_H
21#include <config.h>
22#endif
23
24#include <dbus/dbus.h>
25
26#include <pulsecore/core-util.h>
27#include <pulsecore/dbus-util.h>
28
29#include "iface-device-port.h"
30
31#define OBJECT_NAME "port"
32
33static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata);
34static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata);
35static void handle_get_description(DBusConnection *conn, DBusMessage *msg, void *userdata);
36static void handle_get_priority(DBusConnection *conn, DBusMessage *msg, void *userdata);
37static void handle_get_available(DBusConnection *conn, DBusMessage *msg, void *userdata);
38
39static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata);
40
41struct pa_dbusiface_device_port {
42    uint32_t index;
43    pa_device_port *port;
44    char *path;
45
46    pa_hook_slot *available_changed_slot;
47
48    pa_dbus_protocol *dbus_protocol;
49};
50
51enum property_handler_index {
52    PROPERTY_HANDLER_INDEX,
53    PROPERTY_HANDLER_NAME,
54    PROPERTY_HANDLER_DESCRIPTION,
55    PROPERTY_HANDLER_PRIORITY,
56    PROPERTY_HANDLER_AVAILABLE,
57    PROPERTY_HANDLER_MAX
58};
59
60static pa_dbus_property_handler property_handlers[PROPERTY_HANDLER_MAX] = {
61    [PROPERTY_HANDLER_INDEX]       = { .property_name = "Index",       .type = "u", .get_cb = handle_get_index,       .set_cb = NULL },
62    [PROPERTY_HANDLER_NAME]        = { .property_name = "Name",        .type = "s", .get_cb = handle_get_name,        .set_cb = NULL },
63    [PROPERTY_HANDLER_DESCRIPTION] = { .property_name = "Description", .type = "s", .get_cb = handle_get_description, .set_cb = NULL },
64    [PROPERTY_HANDLER_PRIORITY]    = { .property_name = "Priority",    .type = "u", .get_cb = handle_get_priority,    .set_cb = NULL },
65    [PROPERTY_HANDLER_AVAILABLE]   = { .property_name = "Available",   .type = "u", .get_cb = handle_get_available,   .set_cb = NULL }
66};
67
68enum signal_index {
69    SIGNAL_AVAILABLE_CHANGED,
70    SIGNAL_MAX
71};
72
73static pa_dbus_arg_info available_changed_args[] = { { "available", "u", NULL } };
74
75static pa_dbus_signal_info signals[SIGNAL_MAX] = {
76    [SIGNAL_AVAILABLE_CHANGED] = { .name = "AvailableChanged", .arguments = available_changed_args, .n_arguments = 1 }
77};
78
79static pa_dbus_interface_info port_interface_info = {
80    .name = PA_DBUSIFACE_DEVICE_PORT_INTERFACE,
81    .method_handlers = NULL,
82    .n_method_handlers = 0,
83    .property_handlers = property_handlers,
84    .n_property_handlers = PROPERTY_HANDLER_MAX,
85    .get_all_properties_cb = handle_get_all,
86    .signals = signals,
87    .n_signals = SIGNAL_MAX
88};
89
90static void handle_get_index(DBusConnection *conn, DBusMessage *msg, void *userdata) {
91    pa_dbusiface_device_port *p = userdata;
92
93    pa_assert(conn);
94    pa_assert(msg);
95    pa_assert(p);
96
97    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &p->index);
98}
99
100static void handle_get_name(DBusConnection *conn, DBusMessage *msg, void *userdata) {
101    pa_dbusiface_device_port *p = userdata;
102
103    pa_assert(conn);
104    pa_assert(msg);
105    pa_assert(p);
106
107    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &p->port->name);
108}
109
110static void handle_get_description(DBusConnection *conn, DBusMessage *msg, void *userdata) {
111    pa_dbusiface_device_port *p = userdata;
112
113    pa_assert(conn);
114    pa_assert(msg);
115    pa_assert(p);
116
117    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_STRING, &p->port->description);
118}
119
120static void handle_get_priority(DBusConnection *conn, DBusMessage *msg, void *userdata) {
121    pa_dbusiface_device_port *p = userdata;
122    dbus_uint32_t priority = 0;
123
124    pa_assert(conn);
125    pa_assert(msg);
126    pa_assert(p);
127
128    priority = p->port->priority;
129
130    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &priority);
131}
132
133static void handle_get_available(DBusConnection *conn, DBusMessage *msg, void *userdata) {
134    pa_dbusiface_device_port *p = userdata;
135    dbus_uint32_t available = 0;
136
137    pa_assert(conn);
138    pa_assert(msg);
139    pa_assert(p);
140
141    available = p->port->available;
142
143    pa_dbus_send_basic_variant_reply(conn, msg, DBUS_TYPE_UINT32, &available);
144}
145
146
147static void handle_get_all(DBusConnection *conn, DBusMessage *msg, void *userdata) {
148    pa_dbusiface_device_port *p = userdata;
149    DBusMessage *reply = NULL;
150    DBusMessageIter msg_iter;
151    DBusMessageIter dict_iter;
152    dbus_uint32_t priority = 0;
153
154    pa_assert(conn);
155    pa_assert(msg);
156    pa_assert(p);
157
158    priority = p->port->priority;
159
160    pa_assert_se((reply = dbus_message_new_method_return(msg)));
161
162    dbus_message_iter_init_append(reply, &msg_iter);
163    pa_assert_se(dbus_message_iter_open_container(&msg_iter, DBUS_TYPE_ARRAY, "{sv}", &dict_iter));
164
165    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_INDEX].property_name, DBUS_TYPE_UINT32, &p->index);
166    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_NAME].property_name, DBUS_TYPE_STRING, &p->port->name);
167    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_DESCRIPTION].property_name, DBUS_TYPE_STRING, &p->port->description);
168    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_PRIORITY].property_name, DBUS_TYPE_UINT32, &priority);
169    pa_dbus_append_basic_variant_dict_entry(&dict_iter, property_handlers[PROPERTY_HANDLER_AVAILABLE].property_name, DBUS_TYPE_UINT32, &p->port->available);
170
171    pa_assert_se(dbus_message_iter_close_container(&msg_iter, &dict_iter));
172
173    pa_assert_se(dbus_connection_send(conn, reply, NULL));
174    dbus_message_unref(reply);
175}
176
177static pa_hook_result_t available_changed_cb(void *hook_data, void *call_data, void *slot_data) {
178    pa_dbusiface_device_port *p = slot_data;
179    pa_device_port *port = call_data;
180    DBusMessage *signal_msg;
181    uint32_t available;
182
183    pa_assert(p);
184    pa_assert(port);
185
186    if(p->port != port)
187        return PA_HOOK_OK;
188
189    available = port->available;
190
191    pa_assert_se(signal_msg = dbus_message_new_signal(p->path,
192                                                      PA_DBUSIFACE_DEVICE_PORT_INTERFACE,
193                                                      signals[SIGNAL_AVAILABLE_CHANGED].name));
194    pa_assert_se(dbus_message_append_args(signal_msg, DBUS_TYPE_UINT32, &available, DBUS_TYPE_INVALID));
195
196    pa_dbus_protocol_send_signal(p->dbus_protocol, signal_msg);
197    dbus_message_unref(signal_msg);
198
199    return PA_HOOK_OK;
200}
201
202
203pa_dbusiface_device_port *pa_dbusiface_device_port_new(
204        pa_dbusiface_device *device,
205        pa_core *core,
206        pa_device_port *port,
207        uint32_t idx) {
208    pa_dbusiface_device_port *p = NULL;
209
210    pa_assert(device);
211    pa_assert(core);
212    pa_assert(port);
213
214    p = pa_xnew(pa_dbusiface_device_port, 1);
215    p->index = idx;
216    p->port = port;
217    p->path = pa_sprintf_malloc("%s/%s%u", pa_dbusiface_device_get_path(device), OBJECT_NAME, idx);
218    p->dbus_protocol = pa_dbus_protocol_get(core);
219    p->available_changed_slot = pa_hook_connect(&port->core->hooks[PA_CORE_HOOK_PORT_AVAILABLE_CHANGED],
220                                                PA_HOOK_NORMAL, available_changed_cb, p);
221
222    pa_assert_se(pa_dbus_protocol_add_interface(p->dbus_protocol, p->path, &port_interface_info, p) >= 0);
223
224    return p;
225}
226
227void pa_dbusiface_device_port_free(pa_dbusiface_device_port *p) {
228    pa_assert(p);
229
230    pa_assert_se(pa_dbus_protocol_remove_interface(p->dbus_protocol, p->path, port_interface_info.name) >= 0);
231
232    pa_hook_slot_free(p->available_changed_slot);
233    pa_dbus_protocol_unref(p->dbus_protocol);
234
235    pa_xfree(p->path);
236    pa_xfree(p);
237}
238
239const char *pa_dbusiface_device_port_get_path(pa_dbusiface_device_port *p) {
240    pa_assert(p);
241
242    return p->path;
243}
244
245const char *pa_dbusiface_device_port_get_name(pa_dbusiface_device_port *p) {
246    pa_assert(p);
247
248    return p->port->name;
249}
250