]> code.delx.au - pulseaudio/blob - src/pulsecore/pdispatch.c
7d4e8c46c2b4a8d031f5a1e82fdf17801a3592cb
[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/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_callback drain_callback;
211 void *drain_userdata;
212 const pa_creds *creds;
213 pa_bool_t use_rtclock:1;
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 pa_assert(mainloop);
233
234 pa_assert((entries && table) || (!entries && !table));
235
236 pd = pa_xnew(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->drain_callback = NULL;
243 pd->drain_userdata = NULL;
244 pd->creds = NULL;
245 pd->use_rtclock = use_rtclock;
246
247 return pd;
248 }
249
250 static void pdispatch_free(pa_pdispatch *pd) {
251 pa_assert(pd);
252
253 while (pd->replies) {
254 if (pd->replies->free_cb)
255 pd->replies->free_cb(pd->replies->userdata);
256
257 reply_info_free(pd->replies);
258 }
259
260 pa_xfree(pd);
261 }
262
263 static void run_action(pa_pdispatch *pd, struct reply_info *r, uint32_t command, pa_tagstruct *ts) {
264 pa_pdispatch_cb_t callback;
265 void *userdata;
266 uint32_t tag;
267 pa_assert(r);
268
269 pa_pdispatch_ref(pd);
270
271 callback = r->callback;
272 userdata = r->userdata;
273 tag = r->tag;
274
275 reply_info_free(r);
276
277 callback(pd, command, tag, ts, userdata);
278
279 if (pd->drain_callback && !pa_pdispatch_is_pending(pd))
280 pd->drain_callback(pd, pd->drain_userdata);
281
282 pa_pdispatch_unref(pd);
283 }
284
285 int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, const pa_creds *creds, void *userdata) {
286 uint32_t tag, command;
287 pa_tagstruct *ts = NULL;
288 int ret = -1;
289
290 pa_assert(pd);
291 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
292 pa_assert(packet);
293 pa_assert(PA_REFCNT_VALUE(packet) >= 1);
294 pa_assert(packet->data);
295
296 pa_pdispatch_ref(pd);
297
298 if (packet->length <= 8)
299 goto finish;
300
301 ts = pa_tagstruct_new(packet->data, packet->length);
302
303 if (pa_tagstruct_getu32(ts, &command) < 0 ||
304 pa_tagstruct_getu32(ts, &tag) < 0)
305 goto finish;
306
307 #ifdef DEBUG_OPCODES
308 {
309 char t[256];
310 char const *p = NULL;
311
312 if (command >= PA_COMMAND_MAX || !(p = command_names[command]))
313 pa_snprintf((char*) (p = t), sizeof(t), "%u", command);
314
315 pa_log("[%p] Received opcode <%s>", pd, p);
316 }
317 #endif
318
319 pd->creds = creds;
320
321 if (command == PA_COMMAND_ERROR || command == PA_COMMAND_REPLY) {
322 struct reply_info *r;
323
324 for (r = pd->replies; r; r = r->next)
325 if (r->tag == tag)
326 break;
327
328 if (r)
329 run_action(pd, r, command, ts);
330
331 } else if (pd->callback_table && (command < pd->n_commands) && pd->callback_table[command]) {
332 const pa_pdispatch_cb_t *c = pd->callback_table+command;
333
334 (*c)(pd, command, tag, ts, userdata);
335 } else {
336 pa_log("Received unsupported command %u", command);
337 goto finish;
338 }
339
340 ret = 0;
341
342 finish:
343 pd->creds = NULL;
344
345 if (ts)
346 pa_tagstruct_free(ts);
347
348 pa_pdispatch_unref(pd);
349
350 return ret;
351 }
352
353 static void timeout_callback(pa_mainloop_api*m, pa_time_event*e, const struct timeval *t, void *userdata) {
354 struct reply_info*r = userdata;
355
356 pa_assert(r);
357 pa_assert(r->time_event == e);
358 pa_assert(r->pdispatch);
359 pa_assert(r->pdispatch->mainloop == m);
360 pa_assert(r->callback);
361
362 run_action(r->pdispatch, r, PA_COMMAND_TIMEOUT, NULL);
363 }
364
365 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) {
366 struct reply_info *r;
367 struct timeval tv;
368
369 pa_assert(pd);
370 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
371 pa_assert(cb);
372
373 if (!(r = pa_flist_pop(PA_STATIC_FLIST_GET(reply_infos))))
374 r = pa_xnew(struct reply_info, 1);
375
376 r->pdispatch = pd;
377 r->callback = cb;
378 r->userdata = userdata;
379 r->free_cb = free_cb;
380 r->tag = tag;
381
382 pa_assert_se(r->time_event = pd->mainloop->time_new(pd->mainloop, pa_timeval_rtstore(&tv, pa_rtclock_now() + timeout * PA_USEC_PER_SEC, pd->use_rtclock), timeout_callback, r));
383
384 PA_LLIST_PREPEND(struct reply_info, pd->replies, r);
385 }
386
387 int pa_pdispatch_is_pending(pa_pdispatch *pd) {
388 pa_assert(pd);
389 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
390
391 return !!pd->replies;
392 }
393
394 void pa_pdispatch_set_drain_callback(pa_pdispatch *pd, void (*cb)(pa_pdispatch *pd, void *userdata), void *userdata) {
395 pa_assert(pd);
396 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
397 pa_assert(!cb || pa_pdispatch_is_pending(pd));
398
399 pd->drain_callback = cb;
400 pd->drain_userdata = userdata;
401 }
402
403 void pa_pdispatch_unregister_reply(pa_pdispatch *pd, void *userdata) {
404 struct reply_info *r, *n;
405
406 pa_assert(pd);
407 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
408
409 for (r = pd->replies; r; r = n) {
410 n = r->next;
411
412 if (r->userdata == userdata)
413 reply_info_free(r);
414 }
415 }
416
417 void pa_pdispatch_unref(pa_pdispatch *pd) {
418 pa_assert(pd);
419 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
420
421 if (PA_REFCNT_DEC(pd) <= 0)
422 pdispatch_free(pd);
423 }
424
425 pa_pdispatch* pa_pdispatch_ref(pa_pdispatch *pd) {
426 pa_assert(pd);
427 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
428
429 PA_REFCNT_INC(pd);
430 return pd;
431 }
432
433 const pa_creds * pa_pdispatch_creds(pa_pdispatch *pd) {
434 pa_assert(pd);
435 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
436
437 return pd->creds;
438 }