Merge commit 'origin/master-tx'
[platform/upstream/pulseaudio.git] / src / pulsecore / pdispatch.c
1 /***
2   This file is part of PulseAudio.
3
4   Copyright 2004-2006 Lennart Poettering
5   Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
6
7   PulseAudio is free software; you can redistribute it and/or modify
8   it under the terms of the GNU Lesser General Public License as
9   published by the Free Software Foundation; either version 2.1 of the
10   License, or (at your option) any later version.
11
12   PulseAudio is distributed in the hope that it will be useful, but
13   WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   Lesser General Public License for more details.
16
17   You should have received a copy of the GNU Lesser General Public
18   License along with PulseAudio; if not, write to the Free Software
19   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20   USA.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <stdio.h>
28 #include <stdlib.h>
29
30 #include <pulse/timeval.h>
31 #include <pulse/xmalloc.h>
32
33 #include <pulsecore/native-common.h>
34 #include <pulsecore/llist.h>
35 #include <pulsecore/log.h>
36 #include <pulsecore/core-util.h>
37 #include <pulsecore/macro.h>
38 #include <pulsecore/refcnt.h>
39 #include <pulsecore/flist.h>
40
41 #include "pdispatch.h"
42
43 /* #define DEBUG_OPCODES */
44
45 #ifdef DEBUG_OPCODES
46
47 static const char *command_names[PA_COMMAND_MAX] = {
48     /* Generic commands */
49     [PA_COMMAND_ERROR] = "ERROR",
50     [PA_COMMAND_TIMEOUT] = "TIMEOUT",
51     [PA_COMMAND_REPLY] = "REPLY",
52
53     /* CLIENT->SERVER */
54     [PA_COMMAND_CREATE_PLAYBACK_STREAM] = "CREATE_PLAYBACK_STREAM",
55     [PA_COMMAND_DELETE_PLAYBACK_STREAM] = "DELETE_PLAYBACK_STREAM",
56     [PA_COMMAND_CREATE_RECORD_STREAM] = "CREATE_RECORD_STREAM",
57     [PA_COMMAND_DELETE_RECORD_STREAM] = "DELETE_RECORD_STREAM",
58     [PA_COMMAND_AUTH] = "AUTH",
59     [PA_COMMAND_EXIT] = "EXIT",
60     [PA_COMMAND_SET_CLIENT_NAME] = "SET_CLIENT_NAME",
61     [PA_COMMAND_LOOKUP_SINK] = "LOOKUP_SINK",
62     [PA_COMMAND_LOOKUP_SOURCE] = "LOOKUP_SOURCE",
63     [PA_COMMAND_DRAIN_PLAYBACK_STREAM] = "DRAIN_PLAYBACK_STREAM",
64     [PA_COMMAND_STAT] = "STAT",
65     [PA_COMMAND_GET_PLAYBACK_LATENCY] = "GET_PLAYBACK_LATENCY",
66     [PA_COMMAND_CREATE_UPLOAD_STREAM] = "CREATE_UPLOAD_STREAM",
67     [PA_COMMAND_DELETE_UPLOAD_STREAM] = "DELETE_UPLOAD_STREAM",
68     [PA_COMMAND_FINISH_UPLOAD_STREAM] = "FINISH_UPLOAD_STREAM",
69     [PA_COMMAND_PLAY_SAMPLE] = "PLAY_SAMPLE",
70     [PA_COMMAND_REMOVE_SAMPLE] = "REMOVE_SAMPLE",
71
72     [PA_COMMAND_GET_SERVER_INFO] = "GET_SERVER_INFO",
73     [PA_COMMAND_GET_SINK_INFO] = "GET_SINK_INFO",
74     [PA_COMMAND_GET_SINK_INFO_LIST] = "GET_SINK_INFO_LIST",
75     [PA_COMMAND_GET_SOURCE_INFO] = "GET_SOURCE_INFO",
76     [PA_COMMAND_GET_SOURCE_INFO_LIST] = "GET_SOURCE_INFO_LIST",
77     [PA_COMMAND_GET_MODULE_INFO] = "GET_MODULE_INFO",
78     [PA_COMMAND_GET_MODULE_INFO_LIST] = "GET_MODULE_INFO_LIST",
79     [PA_COMMAND_GET_CLIENT_INFO] = "GET_CLIENT_INFO",
80     [PA_COMMAND_GET_CLIENT_INFO_LIST] = "GET_CLIENT_INFO_LIST",
81     [PA_COMMAND_GET_SAMPLE_INFO] = "GET_SAMPLE_INFO",
82     [PA_COMMAND_GET_SAMPLE_INFO_LIST] = "GET_SAMPLE_INFO_LIST",
83     [PA_COMMAND_GET_SINK_INPUT_INFO] = "GET_SINK_INPUT_INFO",
84     [PA_COMMAND_GET_SINK_INPUT_INFO_LIST] = "GET_SINK_INPUT_INFO_LIST",
85     [PA_COMMAND_GET_SOURCE_OUTPUT_INFO] = "GET_SOURCE_OUTPUT_INFO",
86     [PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST] = "GET_SOURCE_OUTPUT_INFO_LIST",
87     [PA_COMMAND_SUBSCRIBE] = "SUBSCRIBE",
88
89     [PA_COMMAND_SET_SINK_VOLUME] = "SET_SINK_VOLUME",
90     [PA_COMMAND_SET_SINK_INPUT_VOLUME] = "SET_SINK_INPUT_VOLUME",
91     [PA_COMMAND_SET_SOURCE_VOLUME] = "SET_SOURCE_VOLME",
92
93     [PA_COMMAND_SET_SINK_MUTE] = "SET_SINK_MUTE",
94     [PA_COMMAND_SET_SOURCE_MUTE] = "SET_SOURCE_MUTE",
95
96     [PA_COMMAND_TRIGGER_PLAYBACK_STREAM] = "TRIGGER_PLAYBACK_STREAM",
97     [PA_COMMAND_FLUSH_PLAYBACK_STREAM] = "FLUSH_PLAYBACK_STREAM",
98     [PA_COMMAND_CORK_PLAYBACK_STREAM] = "CORK_PLAYBACK_STREAM",
99
100     [PA_COMMAND_SET_DEFAULT_SINK] = "SET_DEFAULT_SINK",
101     [PA_COMMAND_SET_DEFAULT_SOURCE] = "SET_DEFAULT_SOURCE",
102
103     [PA_COMMAND_SET_PLAYBACK_STREAM_NAME] = "SET_PLAYBACK_STREAM_NAME",
104     [PA_COMMAND_SET_RECORD_STREAM_NAME] = "SET_RECORD_STREAM_NAME",
105
106     [PA_COMMAND_KILL_CLIENT] = "KILL_CLIENT",
107     [PA_COMMAND_KILL_SINK_INPUT] = "KILL_SINK_INPUT",
108     [PA_COMMAND_KILL_SOURCE_OUTPUT] = "SOURCE_OUTPUT",
109
110     [PA_COMMAND_LOAD_MODULE] = "LOAD_MODULE",
111     [PA_COMMAND_UNLOAD_MODULE] = "UNLOAD_MODULE",
112
113     [PA_COMMAND_ADD_AUTOLOAD___OBSOLETE] = "ADD_AUTOLOAD (obsolete)",
114     [PA_COMMAND_REMOVE_AUTOLOAD___OBSOLETE] = "REMOVE_AUTOLOAD (obsolete)",
115     [PA_COMMAND_GET_AUTOLOAD_INFO___OBSOLETE] = "GET_AUTOLOAD_INFO (obsolete)",
116     [PA_COMMAND_GET_AUTOLOAD_INFO_LIST___OBSOLETE] = "GET_AUTOLOAD_INFO_LIST (obsolete)",
117
118     [PA_COMMAND_GET_RECORD_LATENCY] = "GET_RECORD_LATENCY",
119     [PA_COMMAND_CORK_RECORD_STREAM] = "CORK_RECORD_STREAM",
120     [PA_COMMAND_FLUSH_RECORD_STREAM] = "FLUSH_RECORD_STREAM",
121     [PA_COMMAND_PREBUF_PLAYBACK_STREAM] = "PREBUF_PLAYBACK_STREAM",
122
123     /* SERVER->CLIENT */
124     [PA_COMMAND_REQUEST] = "REQUEST",
125     [PA_COMMAND_OVERFLOW] = "OVERFLOW",
126     [PA_COMMAND_UNDERFLOW] = "UNDERFLOW",
127     [PA_COMMAND_PLAYBACK_STREAM_KILLED] = "PLAYBACK_STREAM_KILLED",
128     [PA_COMMAND_RECORD_STREAM_KILLED] = "RECORD_STREAM_KILLED",
129     [PA_COMMAND_SUBSCRIBE_EVENT] = "SUBSCRIBE_EVENT",
130
131     /* A few more client->server commands */
132
133     /* Supported since protocol v10 (0.9.5) */
134     [PA_COMMAND_MOVE_SINK_INPUT] = "MOVE_SINK_INPUT",
135     [PA_COMMAND_MOVE_SOURCE_OUTPUT] = "MOVE_SOURCE_OUTPUT",
136
137     /* Supported since protocol v11 (0.9.7) */
138     [PA_COMMAND_SET_SINK_INPUT_MUTE] = "SET_SINK_INPUT_MUTE",
139
140     [PA_COMMAND_SUSPEND_SINK] = "SUSPEND_SINK",
141     [PA_COMMAND_SUSPEND_SOURCE] = "SUSPEND_SOURCE",
142
143     /* Supported since protocol v12 (0.9.8) */
144     [PA_COMMAND_SET_PLAYBACK_STREAM_BUFFER_ATTR] = "SET_PLAYBACK_STREAM_BUFFER_ATTR",
145     [PA_COMMAND_SET_RECORD_STREAM_BUFFER_ATTR] = "SET_RECORD_STREAM_BUFFER_ATTR",
146
147     [PA_COMMAND_UPDATE_PLAYBACK_STREAM_SAMPLE_RATE] = "UPDATE_PLAYBACK_STREAM_SAMPLE_RATE",
148     [PA_COMMAND_UPDATE_RECORD_STREAM_SAMPLE_RATE] = "UPDATE_RECORD_STREAM_SAMPLE_RATE",
149
150     /* SERVER->CLIENT */
151     [PA_COMMAND_PLAYBACK_STREAM_SUSPENDED] = "PLAYBACK_STREAM_SUSPENDED",
152     [PA_COMMAND_RECORD_STREAM_SUSPENDED] = "RECORD_STREAM_SUSPENDED",
153     [PA_COMMAND_PLAYBACK_STREAM_MOVED] = "PLAYBACK_STREAM_MOVED",
154     [PA_COMMAND_RECORD_STREAM_MOVED] = "RECORD_STREAM_MOVED",
155
156     /* Supported since protocol v13 (0.9.11) */
157     [PA_COMMAND_UPDATE_RECORD_STREAM_PROPLIST] = "UPDATE_RECORD_STREAM_PROPLIST",
158     [PA_COMMAND_UPDATE_PLAYBACK_STREAM_PROPLIST] = "UPDATE_RECORD_STREAM_PROPLIST",
159     [PA_COMMAND_UPDATE_CLIENT_PROPLIST] = "UPDATE_CLIENT_PROPLIST",
160     [PA_COMMAND_REMOVE_RECORD_STREAM_PROPLIST] = "REMOVE_RECORD_STREAM_PROPLIST",
161     [PA_COMMAND_REMOVE_PLAYBACK_STREAM_PROPLIST] = "REMOVE_PLAYBACK_STREAM_PROPLIST",
162     [PA_COMMAND_REMOVE_CLIENT_PROPLIST] = "REMOVE_CLIENT_PROPLIST",
163
164     /* SERVER->CLIENT */
165     [PA_COMMAND_STARTED] = "STARTED",
166
167     /* Supported since protocol v14 (0.9.12) */
168     [PA_COMMAND_EXTENSION] = "EXTENSION",
169
170
171     [PA_COMMAND_GET_CARD_INFO] = "GET_CARD_INFO",
172     [PA_COMMAND_GET_CARD_INFO_LIST] = "GET_CARD_INFO_LIST",
173     [PA_COMMAND_SET_CARD_PROFILE] = "SET_CARD_PROFILE",
174
175     [PA_COMMAND_CLIENT_EVENT] = "GET_CLIENT_EVENT",
176     [PA_COMMAND_PLAYBACK_STREAM_EVENT] = "PLAYBACK_STREAM_EVENT",
177     [PA_COMMAND_RECORD_STREAM_EVENT] = "RECORD_STREAM_EVENT",
178
179     /* SERVER->CLIENT */
180     [PA_COMMAND_PLAYBACK_BUFFER_ATTR_CHANGED] = "PLAYBACK_BUFFER_ATTR_CHANGED",
181     [PA_COMMAND_RECORD_BUFFER_ATTR_CHANGED] = "RECORD_BUFFER_ATTR_CHANGED"
182 };
183
184 #endif
185
186 PA_STATIC_FLIST_DECLARE(reply_infos, 0, pa_xfree);
187
188 struct reply_info {
189     pa_pdispatch *pdispatch;
190     PA_LLIST_FIELDS(struct reply_info);
191     pa_pdispatch_cb_t callback;
192     void *userdata;
193     pa_free_cb_t free_cb;
194     uint32_t tag;
195     pa_time_event *time_event;
196 };
197
198 struct pa_pdispatch {
199     PA_REFCNT_DECLARE;
200     pa_mainloop_api *mainloop;
201     const pa_pdispatch_cb_t *callback_table;
202     unsigned n_commands;
203     PA_LLIST_HEAD(struct reply_info, replies);
204     pa_pdispatch_drain_callback drain_callback;
205     void *drain_userdata;
206     const pa_creds *creds;
207 };
208
209 static void reply_info_free(struct reply_info *r) {
210     pa_assert(r);
211     pa_assert(r->pdispatch);
212     pa_assert(r->pdispatch->mainloop);
213
214     if (r->time_event)
215         r->pdispatch->mainloop->time_free(r->time_event);
216
217     PA_LLIST_REMOVE(struct reply_info, r->pdispatch->replies, r);
218
219     if (pa_flist_push(PA_STATIC_FLIST_GET(reply_infos), r) < 0)
220         pa_xfree(r);
221 }
222
223 pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *mainloop, const pa_pdispatch_cb_t*table, unsigned entries) {
224     pa_pdispatch *pd;
225     pa_assert(mainloop);
226
227     pa_assert((entries && table) || (!entries && !table));
228
229     pd = pa_xnew(pa_pdispatch, 1);
230     PA_REFCNT_INIT(pd);
231     pd->mainloop = mainloop;
232     pd->callback_table = table;
233     pd->n_commands = entries;
234     PA_LLIST_HEAD_INIT(struct reply_info, pd->replies);
235     pd->drain_callback = NULL;
236     pd->drain_userdata = NULL;
237     pd->creds = NULL;
238
239     return pd;
240 }
241
242 static void pdispatch_free(pa_pdispatch *pd) {
243     pa_assert(pd);
244
245     while (pd->replies) {
246         if (pd->replies->free_cb)
247             pd->replies->free_cb(pd->replies->userdata);
248
249         reply_info_free(pd->replies);
250     }
251
252     pa_xfree(pd);
253 }
254
255 static void run_action(pa_pdispatch *pd, struct reply_info *r, uint32_t command, pa_tagstruct *ts) {
256     pa_pdispatch_cb_t callback;
257     void *userdata;
258     uint32_t tag;
259     pa_assert(r);
260
261     pa_pdispatch_ref(pd);
262
263     callback = r->callback;
264     userdata = r->userdata;
265     tag = r->tag;
266
267     reply_info_free(r);
268
269     callback(pd, command, tag, ts, userdata);
270
271     if (pd->drain_callback && !pa_pdispatch_is_pending(pd))
272         pd->drain_callback(pd, pd->drain_userdata);
273
274     pa_pdispatch_unref(pd);
275 }
276
277 int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, const pa_creds *creds, void *userdata) {
278     uint32_t tag, command;
279     pa_tagstruct *ts = NULL;
280     int ret = -1;
281
282     pa_assert(pd);
283     pa_assert(PA_REFCNT_VALUE(pd) >= 1);
284     pa_assert(packet);
285     pa_assert(PA_REFCNT_VALUE(packet) >= 1);
286     pa_assert(packet->data);
287
288     pa_pdispatch_ref(pd);
289
290     if (packet->length <= 8)
291         goto finish;
292
293     ts = pa_tagstruct_new(packet->data, packet->length);
294
295     if (pa_tagstruct_getu32(ts, &command) < 0 ||
296         pa_tagstruct_getu32(ts, &tag) < 0)
297         goto finish;
298
299 #ifdef DEBUG_OPCODES
300 {
301     char t[256];
302     char const *p = NULL;
303
304     if (command >= PA_COMMAND_MAX || !(p = command_names[command]))
305         pa_snprintf((char*) (p = t), sizeof(t), "%u", command);
306
307     pa_log("[%p] Received opcode <%s>", pd, p);
308 }
309 #endif
310
311     pd->creds = creds;
312
313     if (command == PA_COMMAND_ERROR || command == PA_COMMAND_REPLY) {
314         struct reply_info *r;
315
316         for (r = pd->replies; r; r = r->next)
317             if (r->tag == tag)
318                 break;
319
320         if (r)
321             run_action(pd, r, command, ts);
322
323     } else if (pd->callback_table && (command < pd->n_commands) && pd->callback_table[command]) {
324         const pa_pdispatch_cb_t *c = pd->callback_table+command;
325
326         (*c)(pd, command, tag, ts, userdata);
327     } else {
328         pa_log("Received unsupported command %u", command);
329         goto finish;
330     }
331
332     ret = 0;
333
334 finish:
335     pd->creds = NULL;
336
337     if (ts)
338         pa_tagstruct_free(ts);
339
340     pa_pdispatch_unref(pd);
341
342     return ret;
343 }
344
345 static void timeout_callback(pa_mainloop_api*m, pa_time_event*e, const struct timeval *tv, void *userdata) {
346     struct reply_info*r = userdata;
347
348     pa_assert(r);
349     pa_assert(r->time_event == e);
350     pa_assert(r->pdispatch);
351     pa_assert(r->pdispatch->mainloop == m);
352     pa_assert(r->callback);
353
354     run_action(r->pdispatch, r, PA_COMMAND_TIMEOUT, NULL);
355 }
356
357 void pa_pdispatch_register_reply(pa_pdispatch *pd, uint32_t tag, int timeout, pa_pdispatch_cb_t cb, void *userdata, pa_free_cb_t free_cb) {
358     struct reply_info *r;
359     struct timeval tv;
360
361     pa_assert(pd);
362     pa_assert(PA_REFCNT_VALUE(pd) >= 1);
363     pa_assert(cb);
364
365     if (!(r = pa_flist_pop(PA_STATIC_FLIST_GET(reply_infos))))
366         r = pa_xnew(struct reply_info, 1);
367
368     r->pdispatch = pd;
369     r->callback = cb;
370     r->userdata = userdata;
371     r->free_cb = free_cb;
372     r->tag = tag;
373
374     pa_gettimeofday(&tv);
375     tv.tv_sec += timeout;
376
377     pa_assert_se(r->time_event = pd->mainloop->time_new(pd->mainloop, &tv, timeout_callback, r));
378
379     PA_LLIST_PREPEND(struct reply_info, pd->replies, r);
380 }
381
382 int pa_pdispatch_is_pending(pa_pdispatch *pd) {
383     pa_assert(pd);
384     pa_assert(PA_REFCNT_VALUE(pd) >= 1);
385
386     return !!pd->replies;
387 }
388
389 void pa_pdispatch_set_drain_callback(pa_pdispatch *pd, void (*cb)(pa_pdispatch *pd, void *userdata), void *userdata) {
390     pa_assert(pd);
391     pa_assert(PA_REFCNT_VALUE(pd) >= 1);
392     pa_assert(!cb || pa_pdispatch_is_pending(pd));
393
394     pd->drain_callback = cb;
395     pd->drain_userdata = userdata;
396 }
397
398 void pa_pdispatch_unregister_reply(pa_pdispatch *pd, void *userdata) {
399     struct reply_info *r, *n;
400
401     pa_assert(pd);
402     pa_assert(PA_REFCNT_VALUE(pd) >= 1);
403
404     for (r = pd->replies; r; r = n) {
405         n = r->next;
406
407         if (r->userdata == userdata)
408             reply_info_free(r);
409     }
410 }
411
412 void pa_pdispatch_unref(pa_pdispatch *pd) {
413     pa_assert(pd);
414     pa_assert(PA_REFCNT_VALUE(pd) >= 1);
415
416     if (PA_REFCNT_DEC(pd) <= 0)
417         pdispatch_free(pd);
418 }
419
420 pa_pdispatch* pa_pdispatch_ref(pa_pdispatch *pd) {
421     pa_assert(pd);
422     pa_assert(PA_REFCNT_VALUE(pd) >= 1);
423
424     PA_REFCNT_INC(pd);
425     return pd;
426 }
427
428 const pa_creds * pa_pdispatch_creds(pa_pdispatch *pd) {
429     pa_assert(pd);
430     pa_assert(PA_REFCNT_VALUE(pd) >= 1);
431
432     return pd->creds;
433 }