]> code.delx.au - pulseaudio/blob - src/pulsecore/pdispatch.c
get rid of svn $ keywords
[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 [PA_COMMAND_ERROR] = "ERROR",
49 [PA_COMMAND_TIMEOUT] = "TIMEOUT",
50 [PA_COMMAND_REPLY] = "REPLY",
51 [PA_COMMAND_CREATE_PLAYBACK_STREAM] = "CREATE_PLAYBACK_STREAM",
52 [PA_COMMAND_DELETE_PLAYBACK_STREAM] = "DELETE_PLAYBACK_STREAM",
53 [PA_COMMAND_CREATE_RECORD_STREAM] = "CREATE_RECORD_STREAM",
54 [PA_COMMAND_DELETE_RECORD_STREAM] = "DELETE_RECORD_STREAM",
55 [PA_COMMAND_AUTH] = "AUTH",
56 [PA_COMMAND_REQUEST] = "REQUEST",
57 [PA_COMMAND_EXIT] = "EXIT",
58 [PA_COMMAND_SET_CLIENT_NAME] = "SET_CLIENT_NAME",
59 [PA_COMMAND_LOOKUP_SINK] = "LOOKUP_SINK",
60 [PA_COMMAND_LOOKUP_SOURCE] = "LOOKUP_SOURCE",
61 [PA_COMMAND_DRAIN_PLAYBACK_STREAM] = "DRAIN_PLAYBACK_STREAM",
62 [PA_COMMAND_PLAYBACK_STREAM_KILLED] = "PLAYBACK_STREAM_KILLED",
63 [PA_COMMAND_RECORD_STREAM_KILLED] = "RECORD_STREAM_KILLED",
64 [PA_COMMAND_STAT] = "STAT",
65 [PA_COMMAND_GET_PLAYBACK_LATENCY] = "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 [PA_COMMAND_GET_SERVER_INFO] = "GET_SERVER_INFO",
72 [PA_COMMAND_GET_SINK_INFO] = "GET_SINK_INFO",
73 [PA_COMMAND_GET_SINK_INFO_LIST] = "GET_SINK_INFO_LIST",
74 [PA_COMMAND_GET_SOURCE_INFO] = "GET_SOURCE_INFO",
75 [PA_COMMAND_GET_SOURCE_INFO_LIST] = "GET_SOURCE_INFO_LIST",
76 [PA_COMMAND_GET_MODULE_INFO] = "GET_MODULE_INFO",
77 [PA_COMMAND_GET_MODULE_INFO_LIST] = "GET_MODULE_INFO_LIST",
78 [PA_COMMAND_GET_CLIENT_INFO] = "GET_CLIENT_INFO",
79 [PA_COMMAND_GET_CLIENT_INFO_LIST] = "GET_CLIENT_INFO_LIST",
80 [PA_COMMAND_GET_SAMPLE_INFO] = "GET_SAMPLE_INFO",
81 [PA_COMMAND_GET_SAMPLE_INFO_LIST] = "GET_SAMPLE_INFO_LIST",
82 [PA_COMMAND_GET_SINK_INPUT_INFO] = "GET_SINK_INPUT_INFO",
83 [PA_COMMAND_GET_SINK_INPUT_INFO_LIST] = "GET_SINK_INPUT_INFO_LIST",
84 [PA_COMMAND_GET_SOURCE_OUTPUT_INFO] = "GET_SOURCE_OUTPUT_INFO",
85 [PA_COMMAND_GET_SOURCE_OUTPUT_INFO_LIST] = "GET_SOURCE_OUTPUT_INFO_LIST",
86 [PA_COMMAND_SUBSCRIBE] = "SUBSCRIBE",
87 [PA_COMMAND_SUBSCRIBE_EVENT] = "SUBSCRIBE_EVENT",
88 [PA_COMMAND_SET_SINK_VOLUME] = "SET_SINK_VOLUME",
89 [PA_COMMAND_SET_SINK_INPUT_VOLUME] = "SET_SINK_INPUT_VOLUME",
90 [PA_COMMAND_SET_SOURCE_VOLUME] = "SET_SOURCE_VOLME",
91 [PA_COMMAND_TRIGGER_PLAYBACK_STREAM] = "TRIGGER_PLAYBACK_STREAM",
92 [PA_COMMAND_FLUSH_PLAYBACK_STREAM] = "FLUSH_PLAYBACK_STREAM",
93 [PA_COMMAND_CORK_PLAYBACK_STREAM] = "CORK_PLAYBACK_STREAM",
94 [PA_COMMAND_GET_AUTOLOAD_INFO] = "GET_AUTOLOAD_INFO",
95 [PA_COMMAND_GET_AUTOLOAD_INFO_LIST] = "GET_AUTOLOAD_INFO_LIST",
96 };
97
98 #endif
99
100 PA_STATIC_FLIST_DECLARE(reply_infos, 0, pa_xfree);
101
102 struct reply_info {
103 pa_pdispatch *pdispatch;
104 PA_LLIST_FIELDS(struct reply_info);
105 pa_pdispatch_cb_t callback;
106 void *userdata;
107 pa_free_cb_t free_cb;
108 uint32_t tag;
109 pa_time_event *time_event;
110 };
111
112 struct pa_pdispatch {
113 PA_REFCNT_DECLARE;
114 pa_mainloop_api *mainloop;
115 const pa_pdispatch_cb_t *callback_table;
116 unsigned n_commands;
117 PA_LLIST_HEAD(struct reply_info, replies);
118 pa_pdispatch_drain_callback drain_callback;
119 void *drain_userdata;
120 const pa_creds *creds;
121 };
122
123 static void reply_info_free(struct reply_info *r) {
124 pa_assert(r);
125 pa_assert(r->pdispatch);
126 pa_assert(r->pdispatch->mainloop);
127
128 if (r->time_event)
129 r->pdispatch->mainloop->time_free(r->time_event);
130
131 PA_LLIST_REMOVE(struct reply_info, r->pdispatch->replies, r);
132
133 if (pa_flist_push(PA_STATIC_FLIST_GET(reply_infos), r) < 0)
134 pa_xfree(r);
135 }
136
137 pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *mainloop, const pa_pdispatch_cb_t*table, unsigned entries) {
138 pa_pdispatch *pd;
139 pa_assert(mainloop);
140
141 pa_assert((entries && table) || (!entries && !table));
142
143 pd = pa_xnew(pa_pdispatch, 1);
144 PA_REFCNT_INIT(pd);
145 pd->mainloop = mainloop;
146 pd->callback_table = table;
147 pd->n_commands = entries;
148 PA_LLIST_HEAD_INIT(struct reply_info, pd->replies);
149 pd->drain_callback = NULL;
150 pd->drain_userdata = NULL;
151 pd->creds = NULL;
152
153 return pd;
154 }
155
156 static void pdispatch_free(pa_pdispatch *pd) {
157 pa_assert(pd);
158
159 while (pd->replies) {
160 if (pd->replies->free_cb)
161 pd->replies->free_cb(pd->replies->userdata);
162
163 reply_info_free(pd->replies);
164 }
165
166 pa_xfree(pd);
167 }
168
169 static void run_action(pa_pdispatch *pd, struct reply_info *r, uint32_t command, pa_tagstruct *ts) {
170 pa_pdispatch_cb_t callback;
171 void *userdata;
172 uint32_t tag;
173 pa_assert(r);
174
175 pa_pdispatch_ref(pd);
176
177 callback = r->callback;
178 userdata = r->userdata;
179 tag = r->tag;
180
181 reply_info_free(r);
182
183 callback(pd, command, tag, ts, userdata);
184
185 if (pd->drain_callback && !pa_pdispatch_is_pending(pd))
186 pd->drain_callback(pd, pd->drain_userdata);
187
188 pa_pdispatch_unref(pd);
189 }
190
191 int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, const pa_creds *creds, void *userdata) {
192 uint32_t tag, command;
193 pa_tagstruct *ts = NULL;
194 int ret = -1;
195
196 pa_assert(pd);
197 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
198 pa_assert(packet);
199 pa_assert(PA_REFCNT_VALUE(packet) >= 1);
200 pa_assert(packet->data);
201
202 pa_pdispatch_ref(pd);
203
204 if (packet->length <= 8)
205 goto finish;
206
207 ts = pa_tagstruct_new(packet->data, packet->length);
208
209 if (pa_tagstruct_getu32(ts, &command) < 0 ||
210 pa_tagstruct_getu32(ts, &tag) < 0)
211 goto finish;
212
213 #ifdef DEBUG_OPCODES
214 {
215 char t[256];
216 char const *p;
217 if (!(p = command_names[command]))
218 pa_snprintf((char*) (p = t), sizeof(t), "%u", command);
219
220 pa_log("Recieved opcode <%s>", p);
221 }
222 #endif
223
224 pd->creds = creds;
225
226 if (command == PA_COMMAND_ERROR || command == PA_COMMAND_REPLY) {
227 struct reply_info *r;
228
229 for (r = pd->replies; r; r = r->next)
230 if (r->tag == tag)
231 break;
232
233 if (r)
234 run_action(pd, r, command, ts);
235
236 } else if (pd->callback_table && (command < pd->n_commands) && pd->callback_table[command]) {
237 const pa_pdispatch_cb_t *c = pd->callback_table+command;
238
239 (*c)(pd, command, tag, ts, userdata);
240 } else {
241 pa_log("Recieved unsupported command %u", command);
242 goto finish;
243 }
244
245 ret = 0;
246
247 finish:
248 pd->creds = NULL;
249
250 if (ts)
251 pa_tagstruct_free(ts);
252
253 pa_pdispatch_unref(pd);
254
255 return ret;
256 }
257
258 static void timeout_callback(pa_mainloop_api*m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
259 struct reply_info*r = userdata;
260
261 pa_assert(r);
262 pa_assert(r->time_event == e);
263 pa_assert(r->pdispatch);
264 pa_assert(r->pdispatch->mainloop == m);
265 pa_assert(r->callback);
266
267 run_action(r->pdispatch, r, PA_COMMAND_TIMEOUT, NULL);
268 }
269
270 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) {
271 struct reply_info *r;
272 struct timeval tv;
273
274 pa_assert(pd);
275 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
276 pa_assert(cb);
277
278 if (!(r = pa_flist_pop(PA_STATIC_FLIST_GET(reply_infos))))
279 r = pa_xnew(struct reply_info, 1);
280
281 r->pdispatch = pd;
282 r->callback = cb;
283 r->userdata = userdata;
284 r->free_cb = free_cb;
285 r->tag = tag;
286
287 pa_gettimeofday(&tv);
288 tv.tv_sec += timeout;
289
290 pa_assert_se(r->time_event = pd->mainloop->time_new(pd->mainloop, &tv, timeout_callback, r));
291
292 PA_LLIST_PREPEND(struct reply_info, pd->replies, r);
293 }
294
295 int pa_pdispatch_is_pending(pa_pdispatch *pd) {
296 pa_assert(pd);
297 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
298
299 return !!pd->replies;
300 }
301
302 void pa_pdispatch_set_drain_callback(pa_pdispatch *pd, void (*cb)(pa_pdispatch *pd, void *userdata), void *userdata) {
303 pa_assert(pd);
304 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
305 pa_assert(!cb || pa_pdispatch_is_pending(pd));
306
307 pd->drain_callback = cb;
308 pd->drain_userdata = userdata;
309 }
310
311 void pa_pdispatch_unregister_reply(pa_pdispatch *pd, void *userdata) {
312 struct reply_info *r, *n;
313
314 pa_assert(pd);
315 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
316
317 for (r = pd->replies; r; r = n) {
318 n = r->next;
319
320 if (r->userdata == userdata)
321 reply_info_free(r);
322 }
323 }
324
325 void pa_pdispatch_unref(pa_pdispatch *pd) {
326 pa_assert(pd);
327 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
328
329 if (PA_REFCNT_DEC(pd) <= 0)
330 pdispatch_free(pd);
331 }
332
333 pa_pdispatch* pa_pdispatch_ref(pa_pdispatch *pd) {
334 pa_assert(pd);
335 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
336
337 PA_REFCNT_INC(pd);
338 return pd;
339 }
340
341 const pa_creds * pa_pdispatch_creds(pa_pdispatch *pd) {
342 pa_assert(pd);
343 pa_assert(PA_REFCNT_VALUE(pd) >= 1);
344
345 return pd->creds;
346 }