]> code.delx.au - pulseaudio/blob - src/pulsecore/pdispatch.c
10238acb058bb40a549318b1372fed1f7d31f8be
[pulseaudio] / src / pulsecore / pdispatch.c
1 /* $Id$ */
2
3 /***
4 This file is part of PulseAudio.
5
6 Copyright 2004-2006 Lennart Poettering
7 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
8
9 PulseAudio is free software; you can redistribute it and/or modify
10 it under the terms of the GNU Lesser General Public License as
11 published by the Free Software Foundation; either version 2.1 of the
12 License, or (at your option) any later version.
13
14 PulseAudio is distributed in the hope that it will be useful, but
15 WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 Lesser General Public License for more details.
18
19 You should have received a copy of the GNU Lesser General Public
20 License along with PulseAudio; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
22 USA.
23 ***/
24
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <assert.h>
32
33 #include <pulse/timeval.h>
34 #include <pulse/xmalloc.h>
35
36 #include <pulsecore/native-common.h>
37 #include <pulsecore/llist.h>
38 #include <pulsecore/log.h>
39 #include <pulsecore/core-util.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 struct reply_info {
101 pa_pdispatch *pdispatch;
102 PA_LLIST_FIELDS(struct reply_info);
103 pa_pdispatch_cb_t callback;
104 void *userdata;
105 pa_free_cb_t free_cb;
106 uint32_t tag;
107 pa_time_event *time_event;
108 };
109
110 struct pa_pdispatch {
111 int ref;
112 pa_mainloop_api *mainloop;
113 const pa_pdispatch_cb_t *callback_table;
114 unsigned n_commands;
115 PA_LLIST_HEAD(struct reply_info, replies);
116 pa_pdispatch_drain_callback drain_callback;
117 void *drain_userdata;
118 const pa_creds *creds;
119 };
120
121 static void reply_info_free(struct reply_info *r) {
122 assert(r && r->pdispatch && r->pdispatch->mainloop);
123
124 if (r->time_event)
125 r->pdispatch->mainloop->time_free(r->time_event);
126
127 PA_LLIST_REMOVE(struct reply_info, r->pdispatch->replies, r);
128
129 pa_xfree(r);
130 }
131
132 pa_pdispatch* pa_pdispatch_new(pa_mainloop_api *mainloop, const pa_pdispatch_cb_t*table, unsigned entries) {
133 pa_pdispatch *pd;
134 assert(mainloop);
135
136 assert((entries && table) || (!entries && !table));
137
138 pd = pa_xmalloc(sizeof(pa_pdispatch));
139 pd->ref = 1;
140 pd->mainloop = mainloop;
141 pd->callback_table = table;
142 pd->n_commands = entries;
143 PA_LLIST_HEAD_INIT(struct reply_info, pd->replies);
144 pd->drain_callback = NULL;
145 pd->drain_userdata = NULL;
146 pd->creds = NULL;
147
148 return pd;
149 }
150
151 static void pdispatch_free(pa_pdispatch *pd) {
152 assert(pd);
153
154 while (pd->replies) {
155 if (pd->replies->free_cb)
156 pd->replies->free_cb(pd->replies->userdata);
157
158 reply_info_free(pd->replies);
159 }
160
161 pa_xfree(pd);
162 }
163
164 static void run_action(pa_pdispatch *pd, struct reply_info *r, uint32_t command, pa_tagstruct *ts) {
165 pa_pdispatch_cb_t callback;
166 void *userdata;
167 uint32_t tag;
168 assert(r);
169
170 pa_pdispatch_ref(pd);
171
172 callback = r->callback;
173 userdata = r->userdata;
174 tag = r->tag;
175
176 reply_info_free(r);
177
178 callback(pd, command, tag, ts, userdata);
179
180 if (pd->drain_callback && !pa_pdispatch_is_pending(pd))
181 pd->drain_callback(pd, pd->drain_userdata);
182
183 pa_pdispatch_unref(pd);
184 }
185
186 int pa_pdispatch_run(pa_pdispatch *pd, pa_packet*packet, const pa_creds *creds, void *userdata) {
187 uint32_t tag, command;
188 pa_tagstruct *ts = NULL;
189 int ret = -1;
190 assert(pd && packet && packet->data);
191
192 pa_pdispatch_ref(pd);
193
194 if (packet->length <= 8)
195 goto finish;
196
197 ts = pa_tagstruct_new(packet->data, packet->length);
198 assert(ts);
199
200 if (pa_tagstruct_getu32(ts, &command) < 0 ||
201 pa_tagstruct_getu32(ts, &tag) < 0)
202 goto finish;
203
204 #ifdef DEBUG_OPCODES
205 {
206 char t[256];
207 char const *p;
208 if (!(p = command_names[command]))
209 snprintf((char*) (p = t), sizeof(t), "%u", command);
210
211 pa_log("Recieved opcode <%s>", p);
212 }
213 #endif
214
215 pd->creds = creds;
216
217 if (command == PA_COMMAND_ERROR || command == PA_COMMAND_REPLY) {
218 struct reply_info *r;
219
220 for (r = pd->replies; r; r = r->next)
221 if (r->tag == tag)
222 break;
223
224 if (r)
225 run_action(pd, r, command, ts);
226
227 } else if (pd->callback_table && (command < pd->n_commands) && pd->callback_table[command]) {
228 const pa_pdispatch_cb_t *c = pd->callback_table+command;
229
230 (*c)(pd, command, tag, ts, userdata);
231 } else {
232 pa_log("Recieved unsupported command %u", command);
233 goto finish;
234 }
235
236 ret = 0;
237
238 finish:
239 pd->creds = NULL;
240
241 if (ts)
242 pa_tagstruct_free(ts);
243
244 pa_pdispatch_unref(pd);
245
246 return ret;
247 }
248
249 static void timeout_callback(pa_mainloop_api*m, pa_time_event*e, PA_GCC_UNUSED const struct timeval *tv, void *userdata) {
250 struct reply_info*r = userdata;
251 assert(r && r->time_event == e && r->pdispatch && r->pdispatch->mainloop == m && r->callback);
252
253 run_action(r->pdispatch, r, PA_COMMAND_TIMEOUT, NULL);
254 }
255
256 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) {
257 struct reply_info *r;
258 struct timeval tv;
259 assert(pd && pd->ref >= 1 && cb);
260
261 r = pa_xnew(struct reply_info, 1);
262 r->pdispatch = pd;
263 r->callback = cb;
264 r->userdata = userdata;
265 r->free_cb = free_cb;
266 r->tag = tag;
267
268 pa_gettimeofday(&tv);
269 tv.tv_sec += timeout;
270
271 r->time_event = pd->mainloop->time_new(pd->mainloop, &tv, timeout_callback, r);
272 assert(r->time_event);
273
274 PA_LLIST_PREPEND(struct reply_info, pd->replies, r);
275 }
276
277 int pa_pdispatch_is_pending(pa_pdispatch *pd) {
278 assert(pd);
279
280 return !!pd->replies;
281 }
282
283 void pa_pdispatch_set_drain_callback(pa_pdispatch *pd, void (*cb)(pa_pdispatch *pd, void *userdata), void *userdata) {
284 assert(pd);
285 assert(!cb || pa_pdispatch_is_pending(pd));
286
287 pd->drain_callback = cb;
288 pd->drain_userdata = userdata;
289 }
290
291 void pa_pdispatch_unregister_reply(pa_pdispatch *pd, void *userdata) {
292 struct reply_info *r, *n;
293 assert(pd);
294
295 for (r = pd->replies; r; r = n) {
296 n = r->next;
297
298 if (r->userdata == userdata)
299 reply_info_free(r);
300 }
301 }
302
303 void pa_pdispatch_unref(pa_pdispatch *pd) {
304 assert(pd && pd->ref >= 1);
305
306 if (!(--(pd->ref)))
307 pdispatch_free(pd);
308 }
309
310 pa_pdispatch* pa_pdispatch_ref(pa_pdispatch *pd) {
311 assert(pd && pd->ref >= 1);
312 pd->ref++;
313 return pd;
314 }
315
316 const pa_creds * pa_pdispatch_creds(pa_pdispatch *pd) {
317 assert(pd);
318 assert(pd->ref >= 1);
319
320 return pd->creds;
321 }