]> code.delx.au - pulseaudio/blob - src/pulsecore/pdispatch.c
pdispatch: various modernizations
[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_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 }