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