]> code.delx.au - pulseaudio/blob - src/pulsecore/pdispatch.c
Merge commit 'origin/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 #endif
172
173 PA_STATIC_FLIST_DECLARE(reply_infos, 0, pa_xfree);
174
175 struct reply_info {
176 pa_pdispatch *pdispatch;
177 PA_LLIST_FIELDS(struct reply_info);
178 pa_pdispatch_cb_t callback;
179 void *userdata;
180 pa_free_cb_t free_cb;
181 uint32_t tag;
182 pa_time_event *time_event;
183 };
184
185 struct pa_pdispatch {
186 PA_REFCNT_DECLARE;
187 pa_mainloop_api *mainloop;
188 const pa_pdispatch_cb_t *callback_table;
189 unsigned n_commands;
190 PA_LLIST_HEAD(struct reply_info, replies);
191 pa_pdispatch_drain_callback drain_callback;
192 void *drain_userdata;
193 const pa_creds *creds;
194 };
195
196 static void reply_info_free(struct reply_info *r) {
197 pa_assert(r);
198 pa_assert(r->pdispatch);
199 pa_assert(r->pdispatch->mainloop);
200
201 if (r->time_event)
202 r->pdispatch->mainloop->time_free(r->time_event);
203
204 PA_LLIST_REMOVE(struct reply_info, r->pdispatch->replies, r);
205
206 if (pa_flist_push(PA_STATIC_FLIST_GET(reply_infos), r) < 0)
207 pa_xfree(r);
208 }
209
210 pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *mainloop, const pa_pdispatch_cb_t*table, unsigned entries) {
211 pa_pdispatch *pd;
212 pa_assert(mainloop);
213
214 pa_assert((entries && table) || (!entries && !table));
215
216 pd = pa_xnew(pa_pdispatch, 1);
217 PA_REFCNT_INIT(pd);
218 pd->mainloop = mainloop;
219 pd->callback_table = table;
220 pd->n_commands = entries;
221 PA_LLIST_HEAD_INIT(struct reply_info, pd->replies);
222 pd->drain_callback = NULL;
223 pd->drain_userdata = NULL;
224 pd->creds = NULL;
225
226 return pd;
227 }
228
229 static void pdispatch_free(pa_pdispatch *pd) {
230 pa_assert(pd);
231
232 while (pd->replies) {
233 if (pd->replies->free_cb)
234 pd->replies->free_cb(pd->replies->userdata);
235
236 reply_info_free(pd->replies);
237 }
238
239 pa_xfree(pd);
240 }
241
242 static void run_action(pa_pdispatch *pd, struct reply_info *r, uint32_t command, pa_tagstruct *ts) {
243 pa_pdispatch_cb_t callback;
244 void *userdata;
245 uint32_t tag;
246 pa_assert(r);
247
248 pa_pdispatch_ref(pd);
249
250 callback = r->callback;
251 userdata = r->userdata;
252 tag = r->tag;
253
254 reply_info_free(r);
255
256 callback(pd, command, tag, ts, userdata);
257
258 if (pd->drain_callback && !pa_pdispatch_is_pending(pd))
259 pd->drain_callback(pd, pd->drain_userdata);
260
261 pa_pdispatch_unref(pd);
262 }
263
264 int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, const pa_creds *creds, void *userdata) {
265 uint32_t tag, command;
266 pa_tagstruct *ts = NULL;
267 int ret = -1;
268
269 pa_assert(pd);
270 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
271 pa_assert(packet);
272 pa_assert(PA_REFCNT_VALUE(packet) >= 1);
273 pa_assert(packet->data);
274
275 pa_pdispatch_ref(pd);
276
277 if (packet->length <= 8)
278 goto finish;
279
280 ts = pa_tagstruct_new(packet->data, packet->length);
281
282 if (pa_tagstruct_getu32(ts, &command) < 0 ||
283 pa_tagstruct_getu32(ts, &tag) < 0)
284 goto finish;
285
286 #ifdef DEBUG_OPCODES
287 {
288 char t[256];
289 char const *p = NULL;
290
291 if (command >= PA_COMMAND_MAX || !(p = command_names[command]))
292 pa_snprintf((char*) (p = t), sizeof(t), "%u", command);
293
294 pa_log("[%p] Recieved opcode <%s>", pd, p);
295 }
296 #endif
297
298 pd->creds = creds;
299
300 if (command == PA_COMMAND_ERROR || command == PA_COMMAND_REPLY) {
301 struct reply_info *r;
302
303 for (r = pd->replies; r; r = r->next)
304 if (r->tag == tag)
305 break;
306
307 if (r)
308 run_action(pd, r, command, ts);
309
310 } else if (pd->callback_table && (command < pd->n_commands) && pd->callback_table[command]) {
311 const pa_pdispatch_cb_t *c = pd->callback_table+command;
312
313 (*c)(pd, command, tag, ts, userdata);
314 } else {
315 pa_log("Recieved unsupported command %u", command);
316 goto finish;
317 }
318
319 ret = 0;
320
321 finish:
322 pd->creds = NULL;
323
324 if (ts)
325 pa_tagstruct_free(ts);
326
327 pa_pdispatch_unref(pd);
328
329 return ret;
330 }
331
332 static void timeout_callback(pa_mainloop_api*m, pa_time_event*e, const struct timeval *tv, void *userdata) {
333 struct reply_info*r = userdata;
334
335 pa_assert(r);
336 pa_assert(r->time_event == e);
337 pa_assert(r->pdispatch);
338 pa_assert(r->pdispatch->mainloop == m);
339 pa_assert(r->callback);
340
341 run_action(r->pdispatch, r, PA_COMMAND_TIMEOUT, NULL);
342 }
343
344 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) {
345 struct reply_info *r;
346 struct timeval tv;
347
348 pa_assert(pd);
349 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
350 pa_assert(cb);
351
352 if (!(r = pa_flist_pop(PA_STATIC_FLIST_GET(reply_infos))))
353 r = pa_xnew(struct reply_info, 1);
354
355 r->pdispatch = pd;
356 r->callback = cb;
357 r->userdata = userdata;
358 r->free_cb = free_cb;
359 r->tag = tag;
360
361 pa_gettimeofday(&tv);
362 tv.tv_sec += timeout;
363
364 pa_assert_se(r->time_event = pd->mainloop->time_new(pd->mainloop, &tv, timeout_callback, r));
365
366 PA_LLIST_PREPEND(struct reply_info, pd->replies, r);
367 }
368
369 int pa_pdispatch_is_pending(pa_pdispatch *pd) {
370 pa_assert(pd);
371 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
372
373 return !!pd->replies;
374 }
375
376 void pa_pdispatch_set_drain_callback(pa_pdispatch *pd, void (*cb)(pa_pdispatch *pd, void *userdata), void *userdata) {
377 pa_assert(pd);
378 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
379 pa_assert(!cb || pa_pdispatch_is_pending(pd));
380
381 pd->drain_callback = cb;
382 pd->drain_userdata = userdata;
383 }
384
385 void pa_pdispatch_unregister_reply(pa_pdispatch *pd, void *userdata) {
386 struct reply_info *r, *n;
387
388 pa_assert(pd);
389 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
390
391 for (r = pd->replies; r; r = n) {
392 n = r->next;
393
394 if (r->userdata == userdata)
395 reply_info_free(r);
396 }
397 }
398
399 void pa_pdispatch_unref(pa_pdispatch *pd) {
400 pa_assert(pd);
401 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
402
403 if (PA_REFCNT_DEC(pd) <= 0)
404 pdispatch_free(pd);
405 }
406
407 pa_pdispatch* pa_pdispatch_ref(pa_pdispatch *pd) {
408 pa_assert(pd);
409 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
410
411 PA_REFCNT_INC(pd);
412 return pd;
413 }
414
415 const pa_creds * pa_pdispatch_creds(pa_pdispatch *pd) {
416 pa_assert(pd);
417 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
418
419 return pd->creds;
420 }