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