]> code.delx.au - pulseaudio/blob - src/pulsecore/pdispatch.c
Merge commit 'origin/master' into master-tx
[pulseaudio] / 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 }