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